From 62f89b2886bbe9df82d9b2fdabfe707509d9e0fc Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Tue, 27 Jun 2000 11:36:50 +0000 Subject: [PATCH] Initial revision --- COPYING | 340 ++ INSTALL | 23 + Makefile.am | 21 + README | 131 + TODO | 369 ++ acconfig.h | 16 + apps/silc/Makefile.am | 40 + apps/silc/client.c | 2371 ++++++++++++ apps/silc/client.h | 249 ++ apps/silc/clientconfig.c | 754 ++++ apps/silc/clientconfig.h | 124 + apps/silc/clientutil.c | 444 +++ apps/silc/clientutil.h | 40 + apps/silc/command.c | 584 +++ apps/silc/command.h | 164 + apps/silc/command_reply.c | 551 +++ apps/silc/command_reply.h | 106 + apps/silc/idlist.h | 58 + apps/silc/protocol.c | 464 +++ apps/silc/protocol.h | 64 + apps/silc/pubkey.pub | Bin 0 -> 330 bytes apps/silc/screen.c | 561 +++ apps/silc/screen.h | 122 + apps/silc/silc.c | 262 ++ apps/silc/silc.h | 35 + apps/silc/testi.conf | 21 + apps/silc/testi2.conf | 21 + apps/silcd/Makefile.am | 42 + apps/silcd/command.c | 943 +++++ apps/silcd/command.h | 164 + apps/silcd/command_reply.c | 140 + apps/silcd/command_reply.h | 77 + apps/silcd/idlist.c | 400 ++ apps/silcd/idlist.h | 428 +++ apps/silcd/leevi.conf | 47 + apps/silcd/leevi2.conf | 47 + apps/silcd/protocol.c | 902 +++++ apps/silcd/protocol.h | 83 + apps/silcd/pubkey.pub | Bin 0 -> 330 bytes apps/silcd/route.c | 65 + apps/silcd/route.h | 77 + apps/silcd/server.c | 3726 +++++++++++++++++++ apps/silcd/server.h | 130 + apps/silcd/server_internal.h | 117 + apps/silcd/server_version.c | 31 + apps/silcd/serverconfig.c | 1446 +++++++ apps/silcd/serverconfig.h | 250 ++ apps/silcd/serverid.c | 105 + apps/silcd/serverid.h | 32 + apps/silcd/silc.conf | 44 + apps/silcd/silcd.c | 132 + apps/silcd/silcd.h | 42 + apps/silcd/testi.conf | 45 + apps/silcd/testi2.conf | 47 + config.guess | 997 +++++ config.sub | 979 +++++ configure.in | 147 + doc/CodingStyle | 575 +++ doc/FAQ | 90 + doc/Makefile.am | 40 + doc/Makefile.in | 203 + doc/draft-riikonen-silc-ke-auth-00.nroff | 1038 ++++++ doc/draft-riikonen-silc-pp-00.nroff | 2317 ++++++++++++ doc/draft-riikonen-silc-spec-00.nroff | 2956 +++++++++++++++ doc/example_silc.conf | 53 + doc/example_silcd.conf | 149 + doc/fix.pl | 40 + doc/makerfc | 9 + includes/Makefile.am | 24 + includes/Makefile.in | 209 ++ includes/bitmove.h | 72 + includes/clientincludes.h | 43 + includes/serverincludes.h | 38 + includes/silcdefs.h.in | 231 ++ includes/silcincludes.h | 158 + includes/version.h | 40 + install-sh | 251 ++ lib/Makefile.am | 36 + lib/silccore/Makefile.am | 46 + lib/silccore/id.c | 125 + lib/silccore/id.h | 116 + lib/silccore/idcache.c | 250 ++ lib/silccore/idcache.h | 79 + lib/silccore/silcbuffer.c | 313 ++ lib/silccore/silcbuffer.h | 349 ++ lib/silccore/silcbuffmt.c | 376 ++ lib/silccore/silcbuffmt.h | 191 + lib/silccore/silcbufutil.c | 81 + lib/silccore/silcbufutil.h | 83 + lib/silccore/silcchannel.c | 321 ++ lib/silccore/silcchannel.h | 75 + lib/silccore/silccommand.c | 340 ++ lib/silccore/silccommand.h | 160 + lib/silccore/silcconfig.c | 94 + lib/silccore/silcconfig.h | 29 + lib/silccore/silclog.c | 207 ++ lib/silccore/silclog.h | 104 + lib/silccore/silcmemory.c | 74 + lib/silccore/silcmemory.h | 36 + lib/silccore/silcnet.c | 293 ++ lib/silccore/silcnet.h | 36 + lib/silccore/silcpacket.c | 388 ++ lib/silccore/silcpacket.h | 185 + lib/silccore/silcprotocol.c | 108 + lib/silccore/silcprotocol.h | 128 + lib/silccore/silcschedule.c | 497 +++ lib/silccore/silcschedule.h | 123 + lib/silccore/silcsockconn.c | 66 + lib/silccore/silcsockconn.h | 133 + lib/silccore/silctask.c | 558 +++ lib/silccore/silctask.h | 356 ++ lib/silccore/silcutil.c | 250 ++ lib/silccore/silcutil.h | 33 + lib/silccrypt/Makefile.am | 44 + lib/silccrypt/blowfish.c | 467 +++ lib/silccrypt/blowfish.h | 138 + lib/silccrypt/blowfish_internal.h | 41 + lib/silccrypt/cast.c | 381 ++ lib/silccrypt/cast.h | 149 + lib/silccrypt/cast_internal.h | 39 + lib/silccrypt/ciphers.h | 29 + lib/silccrypt/ciphers_def.h | 33 + lib/silccrypt/crypton.c | 298 ++ lib/silccrypt/crypton.h | 137 + lib/silccrypt/crypton_internal.h | 39 + lib/silccrypt/dfc.c | 410 ++ lib/silccrypt/dfc.h | 149 + lib/silccrypt/dfc_internal.h | 37 + lib/silccrypt/e2.c | 378 ++ lib/silccrypt/e2.h | 146 + lib/silccrypt/e2_internal.h | 34 + lib/silccrypt/loki.c | 287 ++ lib/silccrypt/loki.h | 146 + lib/silccrypt/loki_internal.h | 37 + lib/silccrypt/mars.c | 525 +++ lib/silccrypt/mars.h | 44 + lib/silccrypt/mars_internal.h | 39 + lib/silccrypt/md5.c | 293 ++ lib/silccrypt/md5.h | 44 + lib/silccrypt/md5_internal.h | 24 + lib/silccrypt/none.c | 61 + lib/silccrypt/none.h | 42 + lib/silccrypt/rc5.c | 143 + lib/silccrypt/rc5.h | 134 + lib/silccrypt/rc5_internal.h | 43 + lib/silccrypt/rc6.c | 255 ++ lib/silccrypt/rc6.h | 44 + lib/silccrypt/rc6_internal.h | 39 + lib/silccrypt/rijndael.c | 384 ++ lib/silccrypt/rijndael.h | 149 + lib/silccrypt/rijndael_internal.h | 41 + lib/silccrypt/rsa.c | 560 +++ lib/silccrypt/rsa.h | 55 + lib/silccrypt/rsa_internal.h | 40 + lib/silccrypt/safer.c | 379 ++ lib/silccrypt/safer.h | 146 + lib/silccrypt/safer_internal.h | 38 + lib/silccrypt/serpent.c | 657 ++++ lib/silccrypt/serpent.h | 146 + lib/silccrypt/serpent_internal.h | 37 + lib/silccrypt/sha1.c | 227 ++ lib/silccrypt/sha1.h | 44 + lib/silccrypt/sha1_internal.h | 34 + lib/silccrypt/silccipher.c | 343 ++ lib/silccrypt/silccipher.h | 131 + lib/silccrypt/silchash.c | 291 ++ lib/silccrypt/silchash.h | 92 + lib/silccrypt/silchmac.c | 158 + lib/silccrypt/silchmac.h | 108 + lib/silccrypt/silcpkcs.c | 243 ++ lib/silccrypt/silcpkcs.h | 164 + lib/silccrypt/silcrng.c | 434 +++ lib/silccrypt/silcrng.h | 51 + lib/silccrypt/tests/inst | 7 + lib/silccrypt/tests/inst_rsa | 7 + lib/silccrypt/tests/inst_twofish | 7 + lib/silccrypt/tests/insth | 6 + lib/silccrypt/tests/test_mars.c | 56 + lib/silccrypt/tests/test_rijndael.c | 49 + lib/silccrypt/tests/test_rsa.c | 90 + lib/silccrypt/tests/test_speed.c | 68 + lib/silccrypt/tests/test_twofish.c | 60 + lib/silccrypt/twofish.c | 601 +++ lib/silccrypt/twofish.h | 44 + lib/silccrypt/twofish_internal.h | 41 + lib/silcmath/Makefile.am | 33 + lib/silcmath/modinv.c | 96 + lib/silcmath/modinv.h | 26 + lib/silcmath/silcmp.h | 111 + lib/silcmath/silcprimegen.c | 380 ++ lib/silcmath/silcprimegen.h | 29 + lib/silcsim/Makefile.am | 75 + lib/silcsim/modules/Makefile.am | 22 + lib/silcsim/modules/Makefile.in | 184 + lib/silcsim/silcsim.c | 135 + lib/silcsim/silcsim.h | 78 + lib/silcsim/silcsimutil.c | 48 + lib/silcsim/silcsimutil.h | 30 + lib/silcske/Makefile.am | 32 + lib/silcske/groups.c | 169 + lib/silcske/groups.h | 40 + lib/silcske/groups_internal.h | 42 + lib/silcske/payload.c | 553 +++ lib/silcske/payload.h | 51 + lib/silcske/payload_internal.h | 65 + lib/silcske/silcske.c | 1314 +++++++ lib/silcske/silcske.h | 200 + lib/silcske/silcske_status.h | 44 + lib/zlib/ChangeLog | 471 +++ lib/zlib/FAQ | 72 + lib/zlib/INDEX | 86 + lib/zlib/Make_vms.com | 115 + lib/zlib/Makefile | 174 + lib/zlib/Makefile.in | 174 + lib/zlib/Makefile.riscos | 151 + lib/zlib/README | 148 + lib/zlib/adler32.c | 48 + lib/zlib/algorithm.txt | 213 ++ lib/zlib/amiga/Makefile.pup | 66 + lib/zlib/amiga/Makefile.sas | 64 + lib/zlib/compress.c | 68 + lib/zlib/configure | 212 ++ lib/zlib/contrib/README.contrib | 34 + lib/zlib/contrib/asm386/gvmat32.asm | 559 +++ lib/zlib/contrib/asm386/gvmat32c.c | 200 + lib/zlib/contrib/asm386/mkgvmt32.bat | 1 + lib/zlib/contrib/asm386/zlibvc.def | 74 + lib/zlib/contrib/asm386/zlibvc.dsp | 651 ++++ lib/zlib/contrib/asm386/zlibvc.dsw | 41 + lib/zlib/contrib/asm586/README.586 | 43 + lib/zlib/contrib/asm586/match.S | 354 ++ lib/zlib/contrib/asm686/README.686 | 34 + lib/zlib/contrib/asm686/match.S | 327 ++ lib/zlib/contrib/delphi/zlib.mak | 36 + lib/zlib/contrib/delphi/zlibdef.pas | 169 + lib/zlib/contrib/delphi2/d_zlib.bpr | 224 ++ lib/zlib/contrib/delphi2/d_zlib.cpp | 17 + lib/zlib/contrib/delphi2/readme.txt | 17 + lib/zlib/contrib/delphi2/zlib.bpg | 26 + lib/zlib/contrib/delphi2/zlib.bpr | 225 ++ lib/zlib/contrib/delphi2/zlib.cpp | 22 + lib/zlib/contrib/delphi2/zlib.pas | 534 +++ lib/zlib/contrib/delphi2/zlib32.bpr | 174 + lib/zlib/contrib/delphi2/zlib32.cpp | 42 + lib/zlib/contrib/iostream/test.cpp | 24 + lib/zlib/contrib/iostream/zfstream.cpp | 329 ++ lib/zlib/contrib/iostream/zfstream.h | 142 + lib/zlib/contrib/iostream2/zstream.h | 307 ++ lib/zlib/contrib/iostream2/zstream_test.cpp | 25 + lib/zlib/contrib/minizip/ChangeLogUnzip | 38 + lib/zlib/contrib/minizip/Makefile | 25 + lib/zlib/contrib/minizip/miniunz.c | 508 +++ lib/zlib/contrib/minizip/minizip.c | 302 ++ lib/zlib/contrib/minizip/readme.txt | 37 + lib/zlib/contrib/minizip/unzip.c | 1294 +++++++ lib/zlib/contrib/minizip/unzip.def | 15 + lib/zlib/contrib/minizip/unzip.h | 275 ++ lib/zlib/contrib/minizip/zip.c | 718 ++++ lib/zlib/contrib/minizip/zip.def | 5 + lib/zlib/contrib/minizip/zip.h | 150 + lib/zlib/contrib/minizip/zlibvc.def | 74 + lib/zlib/contrib/minizip/zlibvc.dsp | 651 ++++ lib/zlib/contrib/minizip/zlibvc.dsw | 41 + lib/zlib/contrib/untgz/Makefile | 14 + lib/zlib/contrib/untgz/makefile.w32 | 63 + lib/zlib/contrib/untgz/untgz.c | 522 +++ lib/zlib/contrib/visual-basic.txt | 69 + lib/zlib/crc32.c | 162 + lib/zlib/deflate.c | 1350 +++++++ lib/zlib/deflate.h | 318 ++ lib/zlib/descrip.mms | 48 + lib/zlib/example.c | 556 +++ lib/zlib/gzio.c | 875 +++++ lib/zlib/infblock.c | 398 ++ lib/zlib/infblock.h | 39 + lib/zlib/infcodes.c | 257 ++ lib/zlib/infcodes.h | 27 + lib/zlib/inffast.c | 170 + lib/zlib/inffast.h | 17 + lib/zlib/inffixed.h | 151 + lib/zlib/inflate.c | 366 ++ lib/zlib/inftrees.c | 455 +++ lib/zlib/inftrees.h | 58 + lib/zlib/infutil.c | 87 + lib/zlib/infutil.h | 98 + lib/zlib/maketree.c | 85 + lib/zlib/minigzip.c | 320 ++ lib/zlib/msdos/Makefile.b32 | 104 + lib/zlib/msdos/Makefile.bor | 125 + lib/zlib/msdos/Makefile.dj2 | 100 + lib/zlib/msdos/Makefile.emx | 69 + lib/zlib/msdos/Makefile.msc | 121 + lib/zlib/msdos/Makefile.tc | 108 + lib/zlib/msdos/Makefile.w32 | 97 + lib/zlib/msdos/Makefile.wat | 103 + lib/zlib/msdos/zlib.def | 60 + lib/zlib/msdos/zlib.rc | 32 + lib/zlib/nt/Makefile.emx | 138 + lib/zlib/nt/Makefile.gcc | 87 + lib/zlib/nt/Makefile.nt | 88 + lib/zlib/nt/zlib.dnt | 47 + lib/zlib/os2/Makefile.os2 | 136 + lib/zlib/os2/zlib.def | 51 + lib/zlib/trees.c | 1214 ++++++ lib/zlib/trees.h | 128 + lib/zlib/uncompr.c | 58 + lib/zlib/zconf.h | 279 ++ lib/zlib/zlib.3 | 107 + lib/zlib/zlib.h | 893 +++++ lib/zlib/ztest2188.c | 2 + lib/zlib/ztest30104.c | 2 + lib/zlib/zutil.c | 225 ++ lib/zlib/zutil.h | 220 ++ missing | 190 + mkinstalldirs | 40 + prepare | 33 + prepare-clean | 43 + public_html/about.html | 41 + public_html/copying.html | 386 ++ public_html/features.html | 57 + public_html/history.html | 66 + public_html/index.html | 76 + public_html/silc.jpg | Bin 0 -> 7269 bytes public_html/silc2.jpg | Bin 0 -> 11150 bytes 324 files changed, 72546 insertions(+) create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100644 acconfig.h create mode 100644 apps/silc/Makefile.am create mode 100644 apps/silc/client.c create mode 100644 apps/silc/client.h create mode 100644 apps/silc/clientconfig.c create mode 100644 apps/silc/clientconfig.h create mode 100644 apps/silc/clientutil.c create mode 100644 apps/silc/clientutil.h create mode 100644 apps/silc/command.c create mode 100644 apps/silc/command.h create mode 100644 apps/silc/command_reply.c create mode 100644 apps/silc/command_reply.h create mode 100644 apps/silc/idlist.h create mode 100644 apps/silc/protocol.c create mode 100644 apps/silc/protocol.h create mode 100644 apps/silc/pubkey.pub create mode 100644 apps/silc/screen.c create mode 100644 apps/silc/screen.h create mode 100644 apps/silc/silc.c create mode 100644 apps/silc/silc.h create mode 100644 apps/silc/testi.conf create mode 100644 apps/silc/testi2.conf create mode 100644 apps/silcd/Makefile.am create mode 100644 apps/silcd/command.c create mode 100644 apps/silcd/command.h create mode 100644 apps/silcd/command_reply.c create mode 100644 apps/silcd/command_reply.h create mode 100644 apps/silcd/idlist.c create mode 100644 apps/silcd/idlist.h create mode 100644 apps/silcd/leevi.conf create mode 100644 apps/silcd/leevi2.conf create mode 100644 apps/silcd/protocol.c create mode 100644 apps/silcd/protocol.h create mode 100644 apps/silcd/pubkey.pub create mode 100644 apps/silcd/route.c create mode 100644 apps/silcd/route.h create mode 100644 apps/silcd/server.c create mode 100644 apps/silcd/server.h create mode 100644 apps/silcd/server_internal.h create mode 100644 apps/silcd/server_version.c create mode 100644 apps/silcd/serverconfig.c create mode 100644 apps/silcd/serverconfig.h create mode 100644 apps/silcd/serverid.c create mode 100644 apps/silcd/serverid.h create mode 100644 apps/silcd/silc.conf create mode 100644 apps/silcd/silcd.c create mode 100644 apps/silcd/silcd.h create mode 100644 apps/silcd/testi.conf create mode 100644 apps/silcd/testi2.conf create mode 100755 config.guess create mode 100755 config.sub create mode 100644 configure.in create mode 100644 doc/CodingStyle create mode 100644 doc/FAQ create mode 100644 doc/Makefile.am create mode 100644 doc/Makefile.in create mode 100644 doc/draft-riikonen-silc-ke-auth-00.nroff create mode 100644 doc/draft-riikonen-silc-pp-00.nroff create mode 100644 doc/draft-riikonen-silc-spec-00.nroff create mode 100644 doc/example_silc.conf create mode 100644 doc/example_silcd.conf create mode 100755 doc/fix.pl create mode 100755 doc/makerfc create mode 100644 includes/Makefile.am create mode 100644 includes/Makefile.in create mode 100644 includes/bitmove.h create mode 100644 includes/clientincludes.h create mode 100644 includes/serverincludes.h create mode 100644 includes/silcdefs.h.in create mode 100644 includes/silcincludes.h create mode 100644 includes/version.h create mode 100755 install-sh create mode 100644 lib/Makefile.am create mode 100644 lib/silccore/Makefile.am create mode 100644 lib/silccore/id.c create mode 100644 lib/silccore/id.h create mode 100644 lib/silccore/idcache.c create mode 100644 lib/silccore/idcache.h create mode 100644 lib/silccore/silcbuffer.c create mode 100644 lib/silccore/silcbuffer.h create mode 100644 lib/silccore/silcbuffmt.c create mode 100644 lib/silccore/silcbuffmt.h create mode 100644 lib/silccore/silcbufutil.c create mode 100644 lib/silccore/silcbufutil.h create mode 100644 lib/silccore/silcchannel.c create mode 100644 lib/silccore/silcchannel.h create mode 100644 lib/silccore/silccommand.c create mode 100644 lib/silccore/silccommand.h create mode 100644 lib/silccore/silcconfig.c create mode 100644 lib/silccore/silcconfig.h create mode 100644 lib/silccore/silclog.c create mode 100644 lib/silccore/silclog.h create mode 100644 lib/silccore/silcmemory.c create mode 100644 lib/silccore/silcmemory.h create mode 100644 lib/silccore/silcnet.c create mode 100644 lib/silccore/silcnet.h create mode 100644 lib/silccore/silcpacket.c create mode 100644 lib/silccore/silcpacket.h create mode 100644 lib/silccore/silcprotocol.c create mode 100644 lib/silccore/silcprotocol.h create mode 100644 lib/silccore/silcschedule.c create mode 100644 lib/silccore/silcschedule.h create mode 100644 lib/silccore/silcsockconn.c create mode 100644 lib/silccore/silcsockconn.h create mode 100644 lib/silccore/silctask.c create mode 100644 lib/silccore/silctask.h create mode 100644 lib/silccore/silcutil.c create mode 100644 lib/silccore/silcutil.h create mode 100644 lib/silccrypt/Makefile.am create mode 100644 lib/silccrypt/blowfish.c create mode 100644 lib/silccrypt/blowfish.h create mode 100644 lib/silccrypt/blowfish_internal.h create mode 100644 lib/silccrypt/cast.c create mode 100644 lib/silccrypt/cast.h create mode 100644 lib/silccrypt/cast_internal.h create mode 100644 lib/silccrypt/ciphers.h create mode 100644 lib/silccrypt/ciphers_def.h create mode 100644 lib/silccrypt/crypton.c create mode 100644 lib/silccrypt/crypton.h create mode 100644 lib/silccrypt/crypton_internal.h create mode 100644 lib/silccrypt/dfc.c create mode 100644 lib/silccrypt/dfc.h create mode 100644 lib/silccrypt/dfc_internal.h create mode 100644 lib/silccrypt/e2.c create mode 100644 lib/silccrypt/e2.h create mode 100644 lib/silccrypt/e2_internal.h create mode 100644 lib/silccrypt/loki.c create mode 100644 lib/silccrypt/loki.h create mode 100644 lib/silccrypt/loki_internal.h create mode 100644 lib/silccrypt/mars.c create mode 100644 lib/silccrypt/mars.h create mode 100644 lib/silccrypt/mars_internal.h create mode 100644 lib/silccrypt/md5.c create mode 100644 lib/silccrypt/md5.h create mode 100644 lib/silccrypt/md5_internal.h create mode 100644 lib/silccrypt/none.c create mode 100644 lib/silccrypt/none.h create mode 100644 lib/silccrypt/rc5.c create mode 100644 lib/silccrypt/rc5.h create mode 100644 lib/silccrypt/rc5_internal.h create mode 100644 lib/silccrypt/rc6.c create mode 100644 lib/silccrypt/rc6.h create mode 100644 lib/silccrypt/rc6_internal.h create mode 100644 lib/silccrypt/rijndael.c create mode 100644 lib/silccrypt/rijndael.h create mode 100644 lib/silccrypt/rijndael_internal.h create mode 100644 lib/silccrypt/rsa.c create mode 100644 lib/silccrypt/rsa.h create mode 100644 lib/silccrypt/rsa_internal.h create mode 100644 lib/silccrypt/safer.c create mode 100644 lib/silccrypt/safer.h create mode 100644 lib/silccrypt/safer_internal.h create mode 100644 lib/silccrypt/serpent.c create mode 100644 lib/silccrypt/serpent.h create mode 100644 lib/silccrypt/serpent_internal.h create mode 100644 lib/silccrypt/sha1.c create mode 100644 lib/silccrypt/sha1.h create mode 100644 lib/silccrypt/sha1_internal.h create mode 100644 lib/silccrypt/silccipher.c create mode 100644 lib/silccrypt/silccipher.h create mode 100644 lib/silccrypt/silchash.c create mode 100644 lib/silccrypt/silchash.h create mode 100644 lib/silccrypt/silchmac.c create mode 100644 lib/silccrypt/silchmac.h create mode 100644 lib/silccrypt/silcpkcs.c create mode 100644 lib/silccrypt/silcpkcs.h create mode 100644 lib/silccrypt/silcrng.c create mode 100644 lib/silccrypt/silcrng.h create mode 100644 lib/silccrypt/tests/inst create mode 100644 lib/silccrypt/tests/inst_rsa create mode 100644 lib/silccrypt/tests/inst_twofish create mode 100644 lib/silccrypt/tests/insth create mode 100644 lib/silccrypt/tests/test_mars.c create mode 100644 lib/silccrypt/tests/test_rijndael.c create mode 100644 lib/silccrypt/tests/test_rsa.c create mode 100644 lib/silccrypt/tests/test_speed.c create mode 100644 lib/silccrypt/tests/test_twofish.c create mode 100644 lib/silccrypt/twofish.c create mode 100644 lib/silccrypt/twofish.h create mode 100644 lib/silccrypt/twofish_internal.h create mode 100644 lib/silcmath/Makefile.am create mode 100644 lib/silcmath/modinv.c create mode 100644 lib/silcmath/modinv.h create mode 100644 lib/silcmath/silcmp.h create mode 100644 lib/silcmath/silcprimegen.c create mode 100644 lib/silcmath/silcprimegen.h create mode 100644 lib/silcsim/Makefile.am create mode 100644 lib/silcsim/modules/Makefile.am create mode 100644 lib/silcsim/modules/Makefile.in create mode 100644 lib/silcsim/silcsim.c create mode 100644 lib/silcsim/silcsim.h create mode 100644 lib/silcsim/silcsimutil.c create mode 100644 lib/silcsim/silcsimutil.h create mode 100644 lib/silcske/Makefile.am create mode 100644 lib/silcske/groups.c create mode 100644 lib/silcske/groups.h create mode 100644 lib/silcske/groups_internal.h create mode 100644 lib/silcske/payload.c create mode 100644 lib/silcske/payload.h create mode 100644 lib/silcske/payload_internal.h create mode 100644 lib/silcske/silcske.c create mode 100644 lib/silcske/silcske.h create mode 100644 lib/silcske/silcske_status.h create mode 100644 lib/zlib/ChangeLog create mode 100644 lib/zlib/FAQ create mode 100644 lib/zlib/INDEX create mode 100644 lib/zlib/Make_vms.com create mode 100644 lib/zlib/Makefile create mode 100644 lib/zlib/Makefile.in create mode 100644 lib/zlib/Makefile.riscos create mode 100644 lib/zlib/README create mode 100644 lib/zlib/adler32.c create mode 100644 lib/zlib/algorithm.txt create mode 100644 lib/zlib/amiga/Makefile.pup create mode 100644 lib/zlib/amiga/Makefile.sas create mode 100644 lib/zlib/compress.c create mode 100755 lib/zlib/configure create mode 100644 lib/zlib/contrib/README.contrib create mode 100644 lib/zlib/contrib/asm386/gvmat32.asm create mode 100644 lib/zlib/contrib/asm386/gvmat32c.c create mode 100644 lib/zlib/contrib/asm386/mkgvmt32.bat create mode 100644 lib/zlib/contrib/asm386/zlibvc.def create mode 100644 lib/zlib/contrib/asm386/zlibvc.dsp create mode 100644 lib/zlib/contrib/asm386/zlibvc.dsw create mode 100644 lib/zlib/contrib/asm586/README.586 create mode 100644 lib/zlib/contrib/asm586/match.S create mode 100644 lib/zlib/contrib/asm686/README.686 create mode 100644 lib/zlib/contrib/asm686/match.S create mode 100644 lib/zlib/contrib/delphi/zlib.mak create mode 100644 lib/zlib/contrib/delphi/zlibdef.pas create mode 100644 lib/zlib/contrib/delphi2/d_zlib.bpr create mode 100644 lib/zlib/contrib/delphi2/d_zlib.cpp create mode 100644 lib/zlib/contrib/delphi2/readme.txt create mode 100644 lib/zlib/contrib/delphi2/zlib.bpg create mode 100644 lib/zlib/contrib/delphi2/zlib.bpr create mode 100644 lib/zlib/contrib/delphi2/zlib.cpp create mode 100644 lib/zlib/contrib/delphi2/zlib.pas create mode 100644 lib/zlib/contrib/delphi2/zlib32.bpr create mode 100644 lib/zlib/contrib/delphi2/zlib32.cpp create mode 100644 lib/zlib/contrib/iostream/test.cpp create mode 100644 lib/zlib/contrib/iostream/zfstream.cpp create mode 100644 lib/zlib/contrib/iostream/zfstream.h create mode 100644 lib/zlib/contrib/iostream2/zstream.h create mode 100644 lib/zlib/contrib/iostream2/zstream_test.cpp create mode 100644 lib/zlib/contrib/minizip/ChangeLogUnzip create mode 100644 lib/zlib/contrib/minizip/Makefile create mode 100644 lib/zlib/contrib/minizip/miniunz.c create mode 100644 lib/zlib/contrib/minizip/minizip.c create mode 100644 lib/zlib/contrib/minizip/readme.txt create mode 100644 lib/zlib/contrib/minizip/unzip.c create mode 100644 lib/zlib/contrib/minizip/unzip.def create mode 100644 lib/zlib/contrib/minizip/unzip.h create mode 100644 lib/zlib/contrib/minizip/zip.c create mode 100644 lib/zlib/contrib/minizip/zip.def create mode 100644 lib/zlib/contrib/minizip/zip.h create mode 100644 lib/zlib/contrib/minizip/zlibvc.def create mode 100644 lib/zlib/contrib/minizip/zlibvc.dsp create mode 100644 lib/zlib/contrib/minizip/zlibvc.dsw create mode 100644 lib/zlib/contrib/untgz/Makefile create mode 100644 lib/zlib/contrib/untgz/makefile.w32 create mode 100644 lib/zlib/contrib/untgz/untgz.c create mode 100644 lib/zlib/contrib/visual-basic.txt create mode 100644 lib/zlib/crc32.c create mode 100644 lib/zlib/deflate.c create mode 100644 lib/zlib/deflate.h create mode 100644 lib/zlib/descrip.mms create mode 100644 lib/zlib/example.c create mode 100644 lib/zlib/gzio.c create mode 100644 lib/zlib/infblock.c create mode 100644 lib/zlib/infblock.h create mode 100644 lib/zlib/infcodes.c create mode 100644 lib/zlib/infcodes.h create mode 100644 lib/zlib/inffast.c create mode 100644 lib/zlib/inffast.h create mode 100644 lib/zlib/inffixed.h create mode 100644 lib/zlib/inflate.c create mode 100644 lib/zlib/inftrees.c create mode 100644 lib/zlib/inftrees.h create mode 100644 lib/zlib/infutil.c create mode 100644 lib/zlib/infutil.h create mode 100644 lib/zlib/maketree.c create mode 100644 lib/zlib/minigzip.c create mode 100644 lib/zlib/msdos/Makefile.b32 create mode 100644 lib/zlib/msdos/Makefile.bor create mode 100644 lib/zlib/msdos/Makefile.dj2 create mode 100644 lib/zlib/msdos/Makefile.emx create mode 100644 lib/zlib/msdos/Makefile.msc create mode 100644 lib/zlib/msdos/Makefile.tc create mode 100644 lib/zlib/msdos/Makefile.w32 create mode 100644 lib/zlib/msdos/Makefile.wat create mode 100644 lib/zlib/msdos/zlib.def create mode 100644 lib/zlib/msdos/zlib.rc create mode 100644 lib/zlib/nt/Makefile.emx create mode 100644 lib/zlib/nt/Makefile.gcc create mode 100644 lib/zlib/nt/Makefile.nt create mode 100644 lib/zlib/nt/zlib.dnt create mode 100644 lib/zlib/os2/Makefile.os2 create mode 100644 lib/zlib/os2/zlib.def create mode 100644 lib/zlib/trees.c create mode 100644 lib/zlib/trees.h create mode 100644 lib/zlib/uncompr.c create mode 100644 lib/zlib/zconf.h create mode 100644 lib/zlib/zlib.3 create mode 100644 lib/zlib/zlib.h create mode 100644 lib/zlib/ztest2188.c create mode 100644 lib/zlib/ztest30104.c create mode 100644 lib/zlib/zutil.c create mode 100644 lib/zlib/zutil.h create mode 100755 missing create mode 100755 mkinstalldirs create mode 100755 prepare create mode 100755 prepare-clean create mode 100644 public_html/about.html create mode 100644 public_html/copying.html create mode 100644 public_html/features.html create mode 100644 public_html/history.html create mode 100644 public_html/index.html create mode 100644 public_html/silc.jpg create mode 100644 public_html/silc2.jpg diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..c2d5f4b5 --- /dev/null +++ b/INSTALL @@ -0,0 +1,23 @@ +Installing SILC Developer's Version +=================================== + +./configure +./make + +You should not install the SILC into your system, instead, you should +run it from the current directory. + +To see different compilation options, give, + +./configure --help + + +Generally, developers wants to compile with debugging, in this case, +give, + +./configure --enable-debug + +WARNING: The debugging is very very heavy and you currently cannot turn +it off if you have compiled it with this option. However, if you're +going to develop or debug SILC you whould compile with this option. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..f0436ab9 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +# +# Makefile.am +# +# Author: Pekka Riikonen +# +# Copyright (C) 2000 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +SUBDIRS = lib silcd silc doc includes diff --git a/README b/README new file mode 100644 index 00000000..1953d942 --- /dev/null +++ b/README @@ -0,0 +1,131 @@ +SILC - Secure Internet Live Conferencing +======================================== + +[NOTE: SILC is still in middle of development and this package is known +as Developer's Version which means that the package is in no means stable +or ready to be in production use. This package is for those who wants +to test SILC, find bugs and maybe contribute some time and code for the +SILC project. There is no guarantees that this package even compiles and +even if it compiles there is no guarantees that it would work, and even +if it works there is no guarantees that it would work correctly, and even +if it seems to work correctly it may be just plain luck.] + + +Description +=========== + +SILC (Secure Internet Live Conferencing) is a protocol which provides +secure conferencing services in the Internet over insecure channel. +SILC is IRC like softwarre although internally they are very different. +Biggest similiarity between SILC and IRC is that they both provide +conferencing services and that SILC has almost same commands as IRC. Other +than that they are nothing alike. Biggest differences are that SILC is +secure what IRC is not in any way. The network model is also entirely +different compared to IRC. + + +Features +======== + +Features to be included into the final release of SILC. [Note that the +current Developer's Version does not include all of these features, read +TODO file for more information.] + + o Normal conferencing services such as private messages, channels, + channel messages, etc. All traffic is secured and authenticated. + + o No unique nicknames. There can same nicknames in SILC without + collisions. SILC has unique Client ID's, Server ID's and Channel ID's + to assure that there are no collisions. + + o Secure key exchange and authentication protocol. SILC Key Exchange + protocol provides key material used in the SILC sessions in secure + manner. The protocol is immune for example to man-in-the-middle + attacks. The SILC Authentication protocol provides strong + authentication. Authentication may be based on passphrase or public + key (RSA) authentication. For clients there is an option not to + use authentication when connecting to servers. + + o All traffic is encrypted and authenticated using the best cryptographic + algorithms out there. Command messages, private messages and channel + messages are all protected by encryption. User can set private keys + for both private message and for channels so that even SILC servers do + not know the keys. Cipher keys are, by default, 128 bits in length and + public keys, by default, 1024 bits in length. + + o Supports data compression with GZIP to improve performance. + + o SIM (SILC Module) support. Support for loading of shared objects at + run-time that provides new and extended features to both SILC client + and server. These can provide extra ciphers and extra features to + the software. + + o SILC client can be installed and used without root privileges. + + o SILC client can be configured by system wide configuration files but + with user specific configuration files as well. + + +History +======= + +Even though SILC were just released to the public the idea and the protocol +itself is quite old. I got the idea about SILC in its current form in +the year 1996 and first lines of codes were written in early 1997. This +release is now third rewrite of the SILC. The very first version were +written in 1997 and it included SILC client and very very preliminary +SILC server. The server actually weren't usable but the client looked +pretty much the same as it does now. At that time the SILC also included +RSA implementation and 3DES implementation. The random number generator +that exists in this current release is actually based on the RNG written +in 1997. The RNG written in 1997, on the other hand, were based on +the SSH's random number generator. The RNG has been rewritten twice +since the first version. + +I stopped writing the SILC later in 1997 when I got busy at school and +in work. The pause lasted several months. The development resumed in +1998 when my friend (Juha Räsänen) and I implemented ElGamal algorithm. +I rewrote some other parts as well. However, for the same reasons as +previously the development stopped again. I resumed the development +later in 1998 by doing rewrite of the SILC in C++. This was obviously +a mistake but at that time it seemed like a good idea. Again, in the +winter 1999 I got very busy writing my thesis and was forced to stop the +development again. I also, started a new job in the spring. + +Later, in 1999, I decided that this time I'm going to make it the right +way. C++ was obviously a bad choice so I decided to fall back to plain +C language. I also decided to do complete rewrite and started doing +more thorough planning of what the SILC actually should include. I also +decided that this time it is going to kill me before I stop the +development. I started writing SILC in the weekends and actually +everytime I had some spare time. I also started a new job but I didn't +let that get to my way. The result of this development effort is the +release now in public. + +I've learned a lot by doing the SILC. I guess, when I started it I wasn't +that good of a C programmer. That alone was a reason why SILC hasn't +seen the day of light before now. My programming style has also changed +dramatically during these years. Actually, it has changed couple times +since this last rewrite as well. However, the code style of current SILC +release is quite consistent (actually the coding style SILC has been +written now I've learned in my current job). + +There is probably over 85% of new code in this third rewrite. Rest has +just been copied from the old versions and only minor changes has been +made (like changed function names and overall coding style). I've +preserved the dates of the old files (dating back to 1997) that has +existed in some forms in the old versions. There is a lot of new code but +already I see a lot that needs rewriting. The development continues. + + +Contact +======= + +Feedback and comments are welcome. You can reach me in the following +Address. + +[Note that generally bug reports should not be sent just yet as the +Developer's Version is full of them and the bug hunt has not even started +yet.] + + Pekka Riikonen diff --git a/TODO b/TODO new file mode 100644 index 00000000..ece615c1 --- /dev/null +++ b/TODO @@ -0,0 +1,369 @@ +TODO +==== + +This is more or less complete list of tasks that has to be done before +SILC 1.0 could ever be released. It is clear that the list does not +include all the bugs that exists. At the end of list are tasks that +needs to be done but are probably post 1.0. + +Feel free to contribute if you have the ability and free time - all the +help is really appreciated - and needed. + + - Pekka + +[Latest Note: The protocol has changed a bit in some parts which +causes that the current implementation violates some requirements. +These are not listed here, currently.] + + +New features TODO +================= + + o Extended SIM (SILC Module) support. Currently only SILC Cipher API + and SILC Hash API may be used as SIM's. What I have in mind is to + have extended support for SIM's so that basically any SILC API could + be used as SIM's. This would open tremendous possiblities but + opens also issues on security that needs to be dealt with. + + Some sort of SIM compilation environment should be defined so that + the SIM's could use SILC specific symbols from the modules (which they + cannot do currently). In the future modules could add new features + to SILC easily with this support. I'm more thinking this from client's + perspective to add new features to client (such as IRC support as SIM) + but server should have the support as well. Anyhow, this is an + interesting feature... + + This maybe post 1.0 task - dunno. + + o SIM support for other platforms than just for Linux. Apache has + example code (code that we could use directly pretty easily) for + other platforms. + + o We should replace all short, int, long, unsigned short, unsigned int, + unsigned long with some pre-defined datatypes that really are what + we want on all platforms. int16, uint16, int32, uint32 etc. are + what we could use or maybe SilcInt16, SilcUInt16 etc. Also, boolean + datatype should be defined. + + o More platform supports should be added. The code is pretty much + generic but there are some parts that require porting (SIM). Also, + some support for different platforms is needed into configure.in. + + o SILC requires currently GCC to work because we use GCC specific + compilation options. Generally any compiler that supports inline + functions and can build shared libraries (for SIMs) should work. + These cases should be included into configure.in. + + +TODO In SILC Client +=================== + + o Implement all commands. A lot of commands are still yet to be + implemented. Most of them are trivial but some will require some + planning. Go see the command.c for unimplemented commands. + + o Non-blocking connection on the background must be stopped if some + other connection on same window has established. Now it is possible + that some non-blocking connection timeouts on the background when + we already have a working connection to some other place; things + goes bad. + + o Finish WHOIS, finish JOIN and other commands that are partly + implemented. + + o Input line on UI is buggy. Cursor movement etc bugs. Too lazy to + fix it. + + o Logic for handling multiple same nicknames for example in private + message sending. I guess the logic is done in server side but is + missing from client. + + o Private message key setting is missing and must be implemented. + Currently private messages are encrypted with session keys. This + is required by the protocol. + + o Channel private key setting is missing and must be implemented. + Currently there cannot be private keys for channels. Normal channel + keys (generated by server) are used. This is required by the protocol. + + o Public and private key generation is now done everytime the program + is run. Now, this is only for testing period as I've been lazy to + do it any better for now. This must be fixed. + + o I guess, public key authentication (when connecting to a server) + is not working currently. It is just matter of loading the keys + from file and using them (see corresponding code in server, it should + support public key authentication already). + + o Multiple windows support. Basic support for multiple windows already + exists but a lot is still missing to get it working. Also, some + of the existing stuff probably needs to be tweaked a bit before the + multiple windows support could be done. And of course the actual + commands that control the windows needs to be written (/WINDDOW). + + o Implement /KEYMAP (or similiar) command to remap control and function + keys. + + o Implement /ALIAS command to make command aliases. + + o Implement /set/if/do/while etc as in IRC2. Maybe post 1.0 task. + Some scripting would be good. + + o Connection Authentication request resolving is missing and must be + done. This is required by the protocol. + + o Key Exchange protocol's responder side is missing from client. + Generally it is possible for the client to be responder so it should + be implemented (See corresponding code from server). Error handling + in the KE protocol is also in pretty bad shape in client. + + o Configuration file loading from global and from local dirs. This + is currently missing and I guess the global is only used. Old SILC + version (in 1997) had ~./silc directory that I guess should be done + now as well. The code for handling those exists but not in current + source tree. + + o Configuration file format - could be better. + + o Write help files for commands. Nice format for the help files should + be selected. I'm open for ideas. + + o All allocations and freeing needs to be checked for memory leaks. + Also, return values from various allocations and functions needs to + checked. + + +TODO In SILC Server +=================== + + o Implement all commands on server side. A lot of commands are still yet + to be implemented. Most of them are trivial but some will require some + planning. Go see the command.c for unimplemented commands. + + o DNS/IP lookup blocks the server. This must be fixed. Check the + resolver stuff (resolver(3), resolver(5)). Either we have to do the + own resolver stuff (through scheduler, if possible without writing + too much own stuff) or use threads. + + o Lenght of the packet processing timeouts needs to be checked whether + they are too short or too long. I haven't really tested whether they + are suitable. They should be tested on high load which I haven't done + at all yet. + + o Public and private key generation is now done everytime the program + is run. Now, this is only for testing period as I've been lazy to + do it any better for now. This must be fixed. + + o Server says that it is able to listen on multiple ports but currently + that is bogus. It can, but internals are for single server. + + o Command lagging must implemented. Those commands (all currently) that + has the LAG flag set they must not be allowed to be executed more than + once, say, in 2 seconds. + + o Command flag usage in general is not implemented yet. + + o Client history must be implemented. Protocol says that server must + keep history information about clients for some period of time. + + o Channel flags and user modes on channels are not implemented yet as + /MODE command is not implemented yet in client and server. + + o Protocol execution timeouts are hard coded, should be configurable. + + o Channel message sending routines uses a lot of common code. Should + create a common function for those instead of writing the same code + again everytime, as done now. + + o serverutil.c I guess should be created for util-like functions that + now resides in server.c, which is getting too big. + + o serverconfig.c and the function naming in it is inconsistent. It is + not silc_config_server* it should be silc_server_config*. As should + all the SilcConfigServer* types be SilcServerConfig*. + + o Implement DENY_CONNECTION section in serverconfig.c and in server. + + o Implement REDIRECT_CLIENT section in serverconfig.c and in server. + + o Configuration file format - could be better. + + o IP address fields in configuration file should accept mask format + as well, IP/MASK, and not just plain IP. + + o Connection classes should be actually implemented in serverconfig.c. + They can be defined but they are totally ignored currently. + + o Acceptance of incoming connections (client and server connections) + should be checked before key exchange protocol. Currently it is + checked at the authentication phase after KE, that is ok, but it should + be checked before starting KE, as well. + + o Statistics are totally missing from the server. It would be nice + to gather some statistics. + + o All allocations and freeing needs to be checked for memory leaks. + Also, return values from various allocations and functions needs to + checked. + + +TODO In SILC Libraries +====================== + + o Public key verification in SKE (SILC Key Exchange) protocol is missing, + thus currently we trust on all public keys. This probably doesn't cause + bad problems but the mechanism of verifying it from local database + (from files) needs to be done (it can open man-in-the-middle-attacks). + + o Implement PFS (Perfect Forward Secrecy) flag in SKE (and in client and + server, actually). If PFS is set, re-key must cause new key exchange. + This is required by the SILC protocol. + + o Re-key in general is actually missing (from everywhere) and must be done. + + o SKE does not send correct status types. Types are defined but not + sent. + + o Connection authentication protocol does not send correct status types. + These types are not defined currently at all. + + o PKCS#1 style RSA public key encryption/decryption/sign/verify is + missing, and should be added for interoperability reasons. The thing + I've done now is bad and should be removed as soon as possible (or + the protocol should then state the method of how they should be done). + + o SILC public key file type is bad. I'd like to see PEM encoded files. + I have public domain code for base64 encoding but it needs to be + rewritten. + + o Slow ciphers should be removed. I think we don't need more than + the AES finalists plus blowfish and RC5. + + o These slow ciphers actually don't work currently as I've tested + only the ones that are worth testing. The CBC mode on these slow + ciphers probably don't work. No need to worry, these ciphers should + be removed. + + o Scheduler needs to be analyzed on high load as it might be unfair + towards select() because it may run timeout tasks before select() and + after select(). If it is found to be unfair the timeout task running + before select() should probably be removed. + + o On select() issue; maybe we should use poll() instead if it is + available? poll() doesn't have max fd limit... + + o SIM support for SILC PKCS API needs to made so that they could be + used as SIM's. At the same time some work is required on prime + generation as the way it is done now sucks. Read from code for + more (silcpkcs.h). + + o Compression routines are missing. The protocol supports packet + compression thus it must be implemented. SILC Comp API must be + defined. zlib package is already included into the lib dir (in CVS, + not in distribution), but it is not used yet, and it requires some + tweaking on the Makefiles (we want static lib not shared). + + o Cipher API needs to be made more consistent. Some parts of the + code generated with current Cipher API looks really bad. Same + is with PKCS API, even worse actually. They need to be made + cleaner. Introducing silc_cipher_encrypt/decrypt/set_key etc. + functions (I actually don't understand why have I left these un-done). + + o Scheduler should automatically allocate task queues if NULL pointers + are passed to the silc_schedule_init. Would make initialization + cleaner. + + o Packet processing routines in client and server are actually pretty + much generic and should be moved from the client/server to the library + as generic routines (silc__packet_decrypt_rest* etc). + This requires heavy changes to the client and server. + + o Random Number Generator needs some tweaking. Reading /dev/random may + block resulting slow initialization of RNG. Some other things in the + RNG may block as well. Also, I have some pending changes to the RNG + that needs to be commited (from Schneier's Yarrow-160 paper). They + should make the RNG even better. + + o Logging should be made more generic in a way that application can + set to where the logging is destined to. Now, it is always destined + to stdout (or stderr) which is a bad thing for client. Ie. some + sort of logging registration functions or similiar should be done + (silclog.[ch] in core). The actual output of logs should be done + by callback function in the application not in lib. + + o I don't like the ID cache system currenly implemented. Ugly and + not so good. Must be rewritten very soon. + + o All allocations and freeing needs to be checked for memory leaks. + + o There are also checks missing from allocations whether the allocation + returns valid memory or NULL. These are missing in library as well + in client and server. Either all checks has to be added or we will + have to make sure that silc_*alloc()s always return valid memory + and assert()s if the system's memory allocator (*alloc()) fails. + + o silc_buffer_[un]format() needs to be made more stable as it may + crash the SILC if malformed data is sent as argument. There are a + lot of places in client and server where we trust directly data coming + from network and try to unformat it. The unformatting routine needs + to be able handle situations where data sent is malformed, by mistake + or intentionally. This is important as it is easy to crash the SILC + now by just sending malformed data. Also, in client and server we + must start checking the return value from silc_buffer_[un]format. + + +Other Things TODO +================= + + o Write manuals for server. + + o Write manuals for client. + + o Write SILC Library Reference manual. This would include all the SILC + API's with simple examples how the functions are to be used. This is + pretty easy to create by taking all the functions plus their comments + from source/header files. However, same sort of reference manual + should be written for client and server as well. + + +TODO After 1.0 +============== + + o Pthreads support. A lot of problems are solved with server (and with + client as well) if we add pthread support. We can forget things such + as non-blocking connecting etc, and we can do things such as DNS/IP + lookups async. The server itself also benefits great deal from + threads, especially from performance point of view. + + But, this is not a small task and almost entire SILC Library has to + be made re-entrant. Own API is probably added for the threads support + to make changes in the future as painless as possible. So the API + would have things like silc_mutex_lock, silc_mutex_unlock and + friends... + + o X.509 certificate support. SILC protocol supports certificates and + it would be great to have support for them. This is a big task as + support has to be made for ASN.1 as well. I've looked into OpenSSL + package as it has X.509 certificate support (and ASN.1 as well). + The code does not look very good to my eye but it has some potentials. + This should be looked at more closely. + + Naturally own SILC Certificate API has to be defined regardles what + the actual X.509 library is (OpenSSL X.509 or something else). Other + choice is to write own X.509 library but I'm not going to do it - + I can help to migrate the OpenSSL X.509 into SILC and I can help if + someone would like to write the X.509 library - but I'm not going + to start writing one myself. Anyhow, the OpenSSL X.509 lib should + be checked. + + o SSH2 public keys support. Maybe - not really needed but could be + nice as SSH is widely used all over the place. SILC Protocol + supports SSH2 public keys. + + o IRC support for SILC client. This would be nice to have on client + as it could be used to connect to SILC and IRC. People wouldn't + have to have two different clients when same would work on both. + I'd like to see this done as SIM, after the extended SIM support + has been added to SILC. + + o Cipher optimizations (asm, that this) at least for i386 would be nice. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 00000000..65e1903e --- /dev/null +++ b/acconfig.h @@ -0,0 +1,16 @@ +/* Name of the package. */ +#undef PACKAGE + +/* Version of the package. */ +#undef VERSION + +/* Debugging */ +#undef SILC_DEBUG + +/* Default configuration file */ +#undef SILC_SERVER_CONFIG_FILE + +/* SIM (SILC Module) support */ +#undef SILC_SIM +#undef HAVE_RTLD_NOW +#undef HAVE_RTLD_LAZY diff --git a/apps/silc/Makefile.am b/apps/silc/Makefile.am new file mode 100644 index 00000000..1b358e63 --- /dev/null +++ b/apps/silc/Makefile.am @@ -0,0 +1,40 @@ +# +# Makefile.am +# +# Author: Pekka Riikonen +# +# Copyright (C) 2000 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +bin_PROGRAMS = silc + +silc_SOURCES = \ + silc.c \ + client.c \ + command.c \ + command_reply.c \ + clientconfig.c \ + clientutil.c \ + protocol.c \ + screen.c + +LDADD = -L. -L.. -L../lib -lsilc -lcurses + +EXTRA_DIST = *.h + +INCLUDES = -I. -I.. -I../lib/silccore -I../lib/silccrypt \ + -I../lib/silcmath -I../lib/silcske -I../lib/silcsim \ + -I../includes \ + -I../lib/silcmath/gmp-3.0.1 diff --git a/apps/silc/client.c b/apps/silc/client.c new file mode 100644 index 00000000..80e2784d --- /dev/null +++ b/apps/silc/client.c @@ -0,0 +1,2371 @@ +/* + + client.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" + +/* Static function prototypes */ +static int silc_client_bad_keys(unsigned char key); +static void silc_client_process_message(SilcClient client); +static char *silc_client_parse_command(unsigned char *buffer); + +/* Static task callback prototypes */ +SILC_TASK_CALLBACK(silc_client_update_clock); +SILC_TASK_CALLBACK(silc_client_run_commands); +SILC_TASK_CALLBACK(silc_client_process_key_press); +SILC_TASK_CALLBACK(silc_client_connect_to_server_start); +SILC_TASK_CALLBACK(silc_client_connect_to_server_second); +SILC_TASK_CALLBACK(silc_client_connect_to_server_final); +SILC_TASK_CALLBACK(silc_client_packet_process); +SILC_TASK_CALLBACK(silc_client_packet_parse); + +SilcClientWindow silc_client_create_main_window(SilcClient client); +SilcClientWindow silc_client_add_window(SilcClient client, + int is_current); +void silc_client_packet_parse_type(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_client_private_message_process(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet); + +/* Definitions from version.h */ +extern char *silc_version; +extern char *silc_name; +extern char *silc_fullname; + +/* Allocates new client object. This has to be done before client may + work. After calling this one must call silc_client_init to initialize + the client. */ + +int silc_client_alloc(SilcClient *new_client) +{ + + *new_client = silc_calloc(1, sizeof(**new_client)); + if (*new_client == NULL) { + SILC_LOG_ERROR(("Could not allocate new client object")); + return FALSE; + } + + (*new_client)->input_buffer = NULL; + (*new_client)->screen = NULL; + (*new_client)->windows = NULL; + (*new_client)->windows_count = 0; + (*new_client)->current_win = NULL; + + return TRUE; +} + +/* Free's client object */ + +void silc_client_free(SilcClient client) +{ + if (client) { + silc_free(client); + } +} + +/* Initializes the client. This makes all the necessary steps to make + the client ready to be run. One must call silc_client_run to run the + client. */ + +int silc_client_init(SilcClient client) +{ + + SILC_LOG_DEBUG(("Initializing client")); + assert(client); + + client->username = silc_get_username(); + client->realname = silc_get_real_name(); + + /* Register all configured ciphers, PKCS and hash functions. */ + client->config->client = (void *)client; + silc_client_config_register_ciphers(client->config); + silc_client_config_register_pkcs(client->config); + silc_client_config_register_hashfuncs(client->config); + + /* Initialize hash functions for client to use */ + silc_hash_alloc("md5", &client->md5hash); + silc_hash_alloc("sha1", &client->sha1hash); + + /* Initialize none cipher */ + silc_cipher_alloc("none", &client->none_cipher); + + /* Initialize random number generator */ + client->rng = silc_rng_alloc(); + silc_rng_init(client->rng); + silc_math_primegen_init(); /* XXX */ + +#if 0 + { + SilcCipher twofish; + unsigned char *src, *dst, *dec; + SilcBuffer packet; + int payload_len; + + payload_len = 4 + strlen("pekka riikonen"); + packet = silc_buffer_alloc(payload_len); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(payload_len), + SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT), + SILC_STR_UI_XNSTRING("pekka riikonen", + strlen("pekka riikonen")), + SILC_STR_END); + + silc_cipher_alloc("twofish", &twofish); + twofish->cipher->set_key(twofish->context, "1234567890123456", 16); + twofish->set_iv(twofish, "6543210987654321"); + SILC_LOG_HEXDUMP(("source: len %d", packet->len), + packet->data, packet->len ); + silc_packet_encrypt(twofish, packet, packet->len); + SILC_LOG_HEXDUMP(("encrypted"), packet->data, packet->len); + silc_packet_decrypt(twofish, packet, packet->len); + SILC_LOG_HEXDUMP(("decrypted"), packet->data, packet->len); + + } + + { + SilcCipher cipher1, cipher2; + unsigned char *src, *dst, *dec; + int len = strlen("12345678901234561234567890123456123456789012345612345678901234561234567890123456"); + + src = silc_calloc(len + 1, sizeof(unsigned char)); + dst = silc_calloc(len + 1, sizeof(unsigned char)); + dec = silc_calloc(len + 1, sizeof(unsigned char)); + + memcpy(src, "12345678901234561234567890123456123456789012345612345678901234561234567890123456", len); + + silc_cipher_alloc("twofish", &cipher1); + cipher1->cipher->set_key(cipher1->context, "1234567890123456", 128); + cipher1->set_iv(cipher1, "6543210987654321"); + + silc_cipher_alloc("twofish", &cipher2); + cipher2->cipher->set_key(cipher2->context, "1234567890123456", 128); + cipher2->set_iv(cipher2, "6543210987654321"); + + SILC_LOG_HEXDUMP(("source: %d", len), src, len); + cipher1->cipher->encrypt(cipher1->context, src, src, len, cipher1->iv); + SILC_LOG_HEXDUMP(("encrypted"), src, len); + cipher2->set_iv(cipher2, "6543210987654321"); + cipher2->cipher->decrypt(cipher2->context, src, src, len, cipher2->iv); + SILC_LOG_HEXDUMP(("decrypted"), src, len); + + } +#endif + + /* Register the task queues. In SILC we have by default three task queues. + One task queue for non-timeout tasks which perform different kind of + I/O on file descriptors, timeout task queue for timeout tasks, and, + generic non-timeout task queue whose tasks apply to all connections. */ + silc_task_queue_alloc(&client->io_queue, TRUE); + if (!client->io_queue) { + goto err0; + } + silc_task_queue_alloc(&client->timeout_queue, TRUE); + if (!client->timeout_queue) { + goto err1; + } + silc_task_queue_alloc(&client->generic_queue, TRUE); + if (!client->generic_queue) { + goto err1; + } + + /* Initialize the scheduler */ + silc_schedule_init(client->io_queue, client->timeout_queue, + client->generic_queue, 5000); + + /* Register the main task that is used in client. This received + the key pressings. */ + if (silc_task_register(client->io_queue, fileno(stdin), + silc_client_process_key_press, + (void *)client, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL) == NULL) { + goto err2; + } + + /* Register timeout task that updates clock every minute. */ + if (silc_task_register(client->timeout_queue, 0, + silc_client_update_clock, + (void *)client, + silc_client_time_til_next_min(), 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW) == NULL) { + goto err2; + } + + if (client->config->commands) { + /* Run user configured commands with timeout */ + if (silc_task_register(client->timeout_queue, 0, + silc_client_run_commands, + (void *)client, 0, 1, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW) == NULL) { + goto err2; + } + } + + /* Allocate the input buffer used to save typed characters */ + client->input_buffer = silc_buffer_alloc(SILC_SCREEN_INPUT_WIN_SIZE); + silc_buffer_pull_tail(client->input_buffer, + SILC_BUFFER_END(client->input_buffer)); + + /* Initialize the screen */ + client->screen = silc_screen_init(); + silc_client_create_main_window(client); + client->screen->input_buffer = client->input_buffer->data; + silc_screen_print_coordinates(client->screen, 0); + + return TRUE; + + err0: + silc_task_queue_free(client->timeout_queue); + err1: + silc_task_queue_free(client->io_queue); + err2: + return FALSE; +} + +/* Stops the client. This is called to stop the client and thus to stop + the program. */ + +void silc_client_stop(SilcClient client) +{ + SILC_LOG_DEBUG(("Stopping client")); + + /* Stop the scheduler, although it might be already stopped. This + doesn't hurt anyone. This removes all the tasks and task queues, + as well. */ + silc_schedule_stop(); + silc_schedule_uninit(); + + SILC_LOG_DEBUG(("Client client")); +} + +/* Runs the client. */ + +void silc_client_run(SilcClient client) +{ + SILC_LOG_DEBUG(("Running client")); + + /* Start the scheduler, the heart of the SILC client. When this returns + the program will be terminated. */ + silc_schedule(); +} + +/* Creates the main window used in SILC client. This is called always + at the initialization of the client. If user wants to create more + than one windows a new windows are always created by calling + silc_client_add_window. */ + +SilcClientWindow silc_client_create_main_window(SilcClient client) +{ + SilcClientWindow win; + void *screen; + + SILC_LOG_DEBUG(("Creating main window")); + + assert(client->screen != NULL); + + win = silc_calloc(1, sizeof(*win)); + if (win == NULL) { + SILC_LOG_ERROR(("Could not allocate new window")); + return NULL; + } + + client->screen->u_stat_line.program_name = silc_name; + client->screen->u_stat_line.program_version = silc_version; + + /* Add the pointers */ + win->nickname = silc_get_username(); + win->local_id = NULL; + win->local_id_data = NULL; + win->local_id_data_len = 0; + win->remote_host = NULL; + win->remote_port = -1; + win->sock = NULL; + + /* Create the actual screen */ + screen = (void *)silc_screen_create_output_window(client->screen); + silc_screen_create_input_window(client->screen); + silc_screen_init_upper_status_line(client->screen); + silc_screen_init_output_status_line(client->screen); + win->screen = screen; + + client->screen->bottom_line->nickname = win->nickname; + silc_screen_print_bottom_line(client->screen, 0); + + /* Add the window to windows table */ + client->windows = silc_calloc(1, sizeof(*client->windows)); + client->windows[client->windows_count] = win; + client->windows_count = 1; + + /* Automatically becomes the current active window */ + client->current_win = win; + + return win; +} + +/* Allocates and adds new window to the client. This allocates new + physical window and internal window for connection specific data. + All the connection specific data is always saved into a window + since connection is always associated to a active window. */ + +SilcClientWindow silc_client_add_window(SilcClient client, + int is_current) +{ + SilcClientWindow win; + + assert(client->screen != NULL); + + win = silc_calloc(1, sizeof(*win)); + if (win == NULL) { + SILC_LOG_ERROR(("Could not allocate new window")); + return NULL; + } + + /* Add the pointers */ + win->screen = silc_screen_add_output_window(client->screen); + win->sock = NULL; + + /* Add the window to windows table */ + client->windows = silc_realloc(client->windows, sizeof(*client->windows) + * (client->windows_count + 1)); + client->windows[client->windows_count] = win; + client->windows_count++; + + if (is_current == TRUE) + client->current_win = win; + + return win; +} + +/* The main task on SILC client. This processes the key pressings user + has made. */ + +SILC_TASK_CALLBACK(silc_client_process_key_press) +{ + SilcClient client = (SilcClient)context; + int c; + + /* There is data pending in stdin, this gets it directly */ + c = wgetch(client->screen->input_win); + if (silc_client_bad_keys(c)) + return; + + SILC_LOG_DEBUG(("Pressed key: %d", c)); + + switch(c) { + /* + * Special character handling + */ + case KEY_UP: + case KEY_DOWN: + break; + case KEY_RIGHT: + /* Right arrow */ + SILC_LOG_DEBUG(("RIGHT")); + silc_screen_input_cursor_right(client->screen); + break; + case KEY_LEFT: + /* Left arrow */ + SILC_LOG_DEBUG(("LEFT")); + silc_screen_input_cursor_left(client->screen); + break; + case KEY_BACKSPACE: + case KEY_DC: + case '\177': + case '\b': + /* Backspace */ + silc_screen_input_backspace(client->screen); + break; + case '\011': + /* Tabulator */ + break; + case KEY_IC: + /* Insert switch. Turns on/off insert on input window */ + silc_screen_input_insert(client->screen); + break; + case CTRL('j'): + case '\r': + /* Enter, Return. User pressed enter we are ready to + process the message. */ + silc_client_process_message(client); + silc_screen_input_reset(client->screen); + break; + case CTRL('l'): + /* Refresh screen, Ctrl^l */ + silc_screen_refresh_all(client->screen); + break; + case CTRL('a'): + case KEY_HOME: + case KEY_BEG: + /* Beginning, Home */ + silc_screen_input_cursor_home(client->screen); + break; + case CTRL('e'): + case KEY_END: + /* End */ + silc_screen_input_cursor_end(client->screen); + break; + case KEY_LL: + /* End */ + break; + case CTRL('g'): + /* Bell, Ctrl^g */ + beep(); + break; + case KEY_DL: + case CTRL('u'): + /* Delete line */ + break; + default: + /* + * Other characters + */ + if (c < 32) { + /* Control codes are printed as reversed */ + c = (c & 127) | 64; + wattron(client->screen->input_win, A_REVERSE); + silc_screen_input_print(client->screen, c); + wattroff(client->screen->input_win, A_REVERSE); + } else { + /* Normal character */ + silc_screen_input_print(client->screen, c); + } + } + + silc_screen_print_coordinates(client->screen, 0); + silc_screen_refresh_win(client->screen->input_win); +} + +static int silc_client_bad_keys(unsigned char key) +{ + /* these are explained in curses.h */ + switch(key) { + case KEY_SF: + case KEY_SR: + case KEY_NPAGE: + case KEY_PPAGE: + case KEY_PRINT: + case KEY_A1: + case KEY_A3: + case KEY_B2: + case KEY_C1: + case KEY_C3: + case KEY_UNDO: + case KEY_EXIT: + case '\v': /* VT */ + case '\E': /* we ignore ESC */ + return TRUE; + default: + return FALSE; + } +} + +/* Processes messages user has typed on the screen. This either sends + a packet out to network or if command were written executes it. */ + +static void silc_client_process_message(SilcClient client) +{ + unsigned char *data; + unsigned int len; + + SILC_LOG_DEBUG(("Start")); + + data = client->input_buffer->data; + len = strlen(data); + + if (data[0] == '/' && data[1] != ' ') { + /* Command */ + unsigned int argc = 0; + unsigned char **argv, *tmpcmd; + unsigned int *argv_lens, *argv_types; + SilcClientCommand *cmd; + SilcClientCommandContext ctx; + + /* Get the command */ + tmpcmd = silc_client_parse_command(data); + + /* Find command match */ + for (cmd = silc_command_list; cmd->name; cmd++) { + if (!strcmp(cmd->name, tmpcmd)) + break; + } + + if (cmd->name == NULL) { + silc_say(client, "Invalid command: %s", tmpcmd); + silc_free(tmpcmd); + goto out; + } + + /* Now parse all arguments */ + silc_client_parse_command_line(data, &argv, &argv_lens, + &argv_types, &argc, cmd->max_args); + silc_free(tmpcmd); + + SILC_LOG_DEBUG(("Exeuting command: %s", cmd->name)); + + /* Allocate command context. This and its internals must be free'd + by the command routine receiving it. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->sock = client->current_win->sock; + ctx->argc = argc; + ctx->argv = argv; + ctx->argv_lens = argv_lens; + ctx->argv_types = argv_types; + + /* Execute command */ + (*cmd->cb)(ctx); + + } else { + /* Normal message to a channel */ + if (len && client->current_win->current_channel && + client->current_win->current_channel->on_channel == TRUE) { + silc_print(client, "> %s", data); + silc_client_packet_send_to_channel(client, + client->current_win->sock, + client->current_win->current_channel, + data, strlen(data), TRUE); + } + } + + out: + /* Clear the input buffer */ + silc_buffer_clear(client->input_buffer); + silc_buffer_pull_tail(client->input_buffer, + SILC_BUFFER_END(client->input_buffer)); +} + +/* Returns the command fetched from user typed command line */ + +static char *silc_client_parse_command(unsigned char *buffer) +{ + char *ret; + const char *cp = buffer; + int len; + + len = strcspn(cp, " "); + ret = silc_to_upper((char *)++cp); + ret[len - 1] = 0; + + return ret; +} + +/* Parses user typed command line. At most `max_args' is taken. Rest + of the line will be allocated as the last argument if there are more + than `max_args' arguments in the line. Note that the command name + is counted as one argument and is saved. */ + +void silc_client_parse_command_line(unsigned char *buffer, + unsigned char ***parsed, + unsigned int **parsed_lens, + unsigned int **parsed_types, + unsigned int *parsed_num, + unsigned int max_args) +{ + int i, len = 0; + int argc = 0; + const char *cp = buffer; + + /* Take the '/' away */ + cp++; + + *parsed = silc_calloc(1, sizeof(**parsed)); + *parsed_lens = silc_calloc(1, sizeof(**parsed_lens)); + + /* Get the command first */ + len = strcspn(cp, " "); + (*parsed)[0] = silc_to_upper((char *)cp); + (*parsed_lens)[0] = len; + cp += len + 1; + argc++; + + /* Parse arguments */ + if (strchr(cp, ' ') || strlen(cp) != 0) { + for (i = 1; i < max_args; i++) { + + if (i != max_args - 1) + len = strcspn(cp, " "); + else + len = strlen(cp); + + *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1)); + *parsed_lens = silc_realloc(*parsed_lens, + sizeof(**parsed_lens) * (argc + 1)); + (*parsed)[argc] = silc_calloc(len + 1, sizeof(char)); + memcpy((*parsed)[argc], cp, len); + (*parsed_lens)[argc] = len; + argc++; + + cp += len; + if (strlen(cp) == 0) + break; + else + cp++; + } + } + + /* Save argument types. Protocol defines all argument types but + this implementation makes sure that they are always in correct + order hence this simple code. */ + *parsed_types = silc_calloc(argc, sizeof(**parsed_types)); + for (i = 0; i < argc; i++) + (*parsed_types)[i] = i; + + *parsed_num = argc; +} + +/* Updates clock on the screen every minute. */ + +SILC_TASK_CALLBACK(silc_client_update_clock) +{ + SilcClient client = (SilcClient)context; + + /* Update the clock on the screen */ + silc_screen_print_clock(client->screen); + + /* Re-register this same task */ + silc_task_register(qptr, 0, silc_client_update_clock, context, + silc_client_time_til_next_min(), 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + silc_screen_refresh_win(client->screen->input_win); +} + +/* Runs commands user configured in configuration file. This is + called when initializing client. */ + +SILC_TASK_CALLBACK(silc_client_run_commands) +{ + SilcClient client = (SilcClient)context; + SilcClientConfigSectionCommand *cs; + + SILC_LOG_DEBUG(("Start")); + + cs = client->config->commands; + while(cs) { + unsigned int argc = 0; + unsigned char **argv, *tmpcmd; + unsigned int *argv_lens, *argv_types; + SilcClientCommand *cmd; + SilcClientCommandContext ctx; + + /* Get the command */ + tmpcmd = silc_client_parse_command(cs->command); + + for (cmd = silc_command_list; cmd->name; cmd++) { + if (!strcmp(cmd->name, tmpcmd)) + break; + } + + if (cmd->name == NULL) { + silc_say(client, "Invalid command: %s", tmpcmd); + silc_free(tmpcmd); + continue; + } + + /* Now parse all arguments */ + silc_client_parse_command_line(cs->command, &argv, &argv_lens, + &argv_types, &argc, cmd->max_args); + silc_free(tmpcmd); + + SILC_LOG_DEBUG(("Exeuting command: %s", cmd->name)); + + /* Allocate command context. This and its internals must be free'd + by the command routine receiving it. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->sock = client->current_win->sock; + ctx->argc = argc; + ctx->argv = argv; + ctx->argv_lens = argv_lens; + ctx->argv_types = argv_types; + + /* Execute command */ + (*cmd->cb)(ctx); + + cs = cs->next; + } +} + +/* Internal context for connection process. This is needed as we + doing asynchronous connecting. */ +typedef struct { + SilcClient client; + SilcTask task; + int sock; + char *host; + int port; + int tries; +} SilcClientInternalConnectContext; + +static int +silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx) +{ + int sock; + + /* XXX In the future we should give up this non-blocking connect all + together and use threads instead. */ + /* Create connection to server asynchronously */ + sock = silc_net_create_connection_async(ctx->port, ctx->host); + if (sock < 0) + return -1; + + /* Register task that will receive the async connect and will + read the result. */ + ctx->task = silc_task_register(ctx->client->io_queue, sock, + silc_client_connect_to_server_start, + (void *)ctx, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL); + silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE); + silc_schedule_set_listen_fd(sock, ctx->task->iomask); + + ctx->sock = sock; + + return sock; +} + +/* Connects to remote server */ + +int silc_client_connect_to_server(SilcClient client, int port, + char *host) +{ + SilcClientInternalConnectContext *ctx; + + SILC_LOG_DEBUG(("Connecting to port %d of server %s", + port, host)); + + silc_say(client, "Connecting to port %d of server %s", port, host); + + client->current_win->remote_host = strdup(host); + client->current_win->remote_port = port; + + /* Allocate internal context for connection process. This is + needed as we are doing async connecting. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->host = strdup(host); + ctx->port = port; + ctx->tries = 0; + + /* Do the actual connecting process */ + return silc_client_connect_to_server_internal(ctx); +} + +/* Start of the connection to the remote server. This is called after + succesful TCP/IP connection has been established to the remote host. */ + +SILC_TASK_CALLBACK(silc_client_connect_to_server_start) +{ + SilcClientInternalConnectContext *ctx = + (SilcClientInternalConnectContext *)context; + SilcClient client = ctx->client; + SilcProtocol protocol; + SilcClientKEInternalContext *proto_ctx; + int opt, opt_len = sizeof(opt); + + SILC_LOG_DEBUG(("Start")); + + /* Check the socket status as it might be in error */ + getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len); + if (opt != 0) { + if (ctx->tries < 2) { + /* Connection failed but lets try again */ + silc_say(ctx->client, "Could not connect to server %s: %s", + ctx->host, strerror(opt)); + silc_say(client, "Connecting to port %d of server %s resumed", + ctx->port, ctx->host); + + /* Unregister old connection try */ + silc_schedule_unset_listen_fd(fd); + silc_net_close_connection(fd); + silc_task_unregister(client->io_queue, ctx->task); + + /* Try again */ + silc_client_connect_to_server_internal(ctx); + ctx->tries++; + } else { + /* Connection failed and we won't try anymore */ + silc_say(ctx->client, "Could not connect to server %s: %s", + ctx->host, strerror(opt)); + silc_schedule_unset_listen_fd(fd); + silc_net_close_connection(fd); + silc_task_unregister(client->io_queue, ctx->task); + silc_free(ctx); + } + return; + } + + silc_schedule_unset_listen_fd(fd); + silc_task_unregister(client->io_queue, ctx->task); + silc_free(ctx); + + /* Allocate new socket connection object */ + silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, + (void *)client->current_win, + &client->current_win->sock); + if (client->current_win->sock == NULL) { + silc_say(client, "Error: Could not allocate connection socket"); + silc_net_close_connection(fd); + return; + } + client->current_win->sock->hostname = client->current_win->remote_host; + client->current_win->sock->port = client->current_win->remote_port; + + /* Allocate internal Key Exchange context. This is sent to the + protocol as context. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->client = (void *)client; + proto_ctx->sock = client->current_win->sock; + proto_ctx->rng = client->rng; + proto_ctx->responder = FALSE; + + /* Perform key exchange protocol. silc_client_connect_to_server_final + will be called after the protocol is finished. */ + silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, + &protocol, (void *)proto_ctx, + silc_client_connect_to_server_second); + if (!protocol) { + silc_say(client, "Error: Could not start authentication protocol"); + return; + } + client->current_win->sock->protocol = protocol; + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as well. + However, this doesn't set the scheduler for outgoing traffic, it will + be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + context = (void *)client; + SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd); + + /* Execute the protocol */ + protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0); +} + +/* Second part of the connecting to the server. This executed + authentication protocol. */ + +SILC_TASK_CALLBACK(silc_client_connect_to_server_second) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientKEInternalContext *ctx = + (SilcClientKEInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + SilcSocketConnection sock = NULL; + SilcClientConnAuthInternalContext *proto_ctx; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { + /* Error occured during protocol */ + SILC_LOG_DEBUG(("Error during KE protocol")); + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + silc_free(ctx); + sock->protocol = NULL; + return; + } + + /* Allocate internal context for the authentication protocol. This + is sent as context for the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->client = (void *)client; + proto_ctx->sock = sock = ctx->sock; + proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */ + + /* Resolve the authentication method to be used in this connection */ + proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE; + if (client->config->conns) { + SilcClientConfigSectionConnection *conn = NULL; + + /* Check if we find a match from user configured connections */ + conn = silc_client_config_find_connection(client->config, + sock->hostname, + sock->port); + if (conn) { + /* Match found. Use the configured authentication method */ + proto_ctx->auth_meth = conn->auth_meth; + if (conn->auth_data) { + proto_ctx->auth_data = strdup(conn->auth_data); + proto_ctx->auth_data_len = strlen(conn->auth_data); + } + } else { + /* No match found. Resolve by sending AUTH_REQUEST to server */ + proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE; + } + } else { + /* XXX Resolve by sending AUTH_REQUEST to server */ + proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE; + } + + /* Free old protocol as it is finished now */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + silc_free(ctx); + /* silc_free(ctx->keymat....); */ + sock->protocol = NULL; + + /* Allocate the authentication protocol. This is allocated here + but we won't start it yet. We will be receiving party of this + protocol thus we will wait that connecting party will make + their first move. */ + silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, + &sock->protocol, (void *)proto_ctx, + silc_client_connect_to_server_final); + + /* Execute the protocol */ + sock->protocol->execute(client->timeout_queue, 0, sock->protocol, fd, 0, 0); +} + +/* Finalizes the connection to the remote SILC server. This is called + after authentication protocol has been completed. This send our + user information to the server to receive our client ID from + server. */ + +SILC_TASK_CALLBACK(silc_client_connect_to_server_final) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientConnAuthInternalContext *ctx = + (SilcClientConnAuthInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + SilcClientWindow win = (SilcClientWindow)ctx->sock->user_data; + SilcBuffer packet; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { + /* Error occured during protocol */ + SILC_LOG_DEBUG(("Error during authentication protocol")); + silc_protocol_free(protocol); + if (ctx->auth_data) + silc_free(ctx->auth_data); + if (ctx->ske) + silc_ske_free(ctx->ske); + silc_free(ctx); + win->sock->protocol = NULL; + return; + } + + /* Send NEW_CLIENT packet to the server. We will become registered + to the SILC network after sending this packet and we will receive + client ID from the server. */ + packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + + strlen(client->realname)); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(strlen(client->username)), + SILC_STR_UI_XNSTRING(client->username, + strlen(client->username)), + SILC_STR_UI_SHORT(strlen(client->realname)), + SILC_STR_UI_XNSTRING(client->realname, + strlen(client->realname)), + SILC_STR_END); + + /* Send the packet */ + silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT, + NULL, 0, NULL, NULL, + packet->data, packet->len, TRUE); + silc_buffer_free(packet); + + silc_say(client, "Connected to port %d of host %s", + win->remote_port, win->remote_host); + + client->screen->bottom_line->connection = win->remote_host; + silc_screen_print_bottom_line(client->screen, 0); + + silc_protocol_free(protocol); + if (ctx->auth_data) + silc_free(ctx->auth_data); + if (ctx->ske) + silc_ske_free(ctx->ske); + silc_free(ctx); + win->sock->protocol = NULL; +} + +typedef struct { + SilcPacketContext *packetdata; + SilcSocketConnection sock; + SilcClient client; +} SilcClientInternalPacket; + +SILC_TASK_CALLBACK(silc_client_packet_process) +{ + SilcClient client = (SilcClient)context; + SilcSocketConnection sock = NULL; + int ret, packetlen, paddedlen; + + SILC_LOG_DEBUG(("Processing packet")); + + SILC_CLIENT_GET_SOCK(client, fd, sock); + if (sock == NULL) + return; + + /* Packet sending */ + if (type == SILC_TASK_WRITE) { + SILC_LOG_DEBUG(("Writing data to connection")); + + if (sock->outbuf->data - sock->outbuf->head) + silc_buffer_push(sock->outbuf, + sock->outbuf->data - sock->outbuf->head); + + /* Write the packet out to the connection */ + ret = silc_packet_write(fd, sock->outbuf); + + /* If returned -2 could not write to connection now, will do + it later. */ + if (ret == -2) + return; + + /* Error */ + if (ret == -1) + SILC_LOG_ERROR(("Packet dropped")); + + /* The packet has been sent and now it is time to set the connection + back to only for input. When there is again some outgoing data + available for this connection it will be set for output as well. + This call clears the output setting and sets it only for input. */ + SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd); + SILC_UNSET_OUTBUF_PENDING(sock); + + return; + } + + /* Packet receiving */ + if (type == SILC_TASK_READ) { + SILC_LOG_DEBUG(("Reading data from connection")); + + /* Allocate the incoming data buffer if not done already. */ + if (!sock->inbuf) + sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE); + + /* Read some data from connection */ + ret = silc_packet_read(fd, sock->inbuf); + + /* If returned -2 data was not available now, will read it later. */ + if (ret == -2) + return; + + /* Error */ + if (ret == -1) { + SILC_LOG_ERROR(("Packet dropped")); + return; + } + + /* EOF */ + if (ret == 0) { + SILC_LOG_DEBUG(("Read EOF")); + + /* If connection is disconnecting already we will finally + close the connection */ + if (SILC_IS_DISCONNECTING(sock)) { + silc_client_close_connection(client, sock); + return; + } + + silc_say(client, "Connection closed: premature EOF"); + SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock)); + + silc_client_close_connection(client, sock); + return; + } + + /* Check whether we received a whole packet. If reading went without + errors we either read a whole packet or the read packet is + incorrect and will be dropped. */ + SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen); + if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) { + SILC_LOG_DEBUG(("Received incorrect packet, dropped")); + silc_buffer_clear(sock->inbuf); + return; + } + + /* Decrypt a packet coming from server connection */ + if (sock->type == SILC_SOCKET_TYPE_SERVER || + sock->type == SILC_SOCKET_TYPE_ROUTER) { + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcClientInternalPacket *packet; + int mac_len = 0; + + if (win->hmac) + mac_len = win->hmac->hash->hash->hash_len; + + if (sock->inbuf->len - 2 > (paddedlen + mac_len)) { + /* Received possibly many packets at once */ + + while(sock->inbuf->len > 0) { + SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen); + if (sock->inbuf->len < paddedlen) { + SILC_LOG_DEBUG(("Received incorrect packet, dropped")); + return; + } + + paddedlen += 2; + packet = silc_calloc(1, sizeof(*packet)); + packet->client = client; + packet->sock = sock; + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len); + silc_buffer_pull_tail(packet->packetdata->buffer, + SILC_BUFFER_END(packet->packetdata->buffer)); + silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, + paddedlen + mac_len); + + SILC_LOG_HEXDUMP(("Incoming packet, len %d", + packet->packetdata->buffer->len), + packet->packetdata->buffer->data, + packet->packetdata->buffer->len); + SILC_LOG_DEBUG(("Packet from server %s, " + "server type %d, packet length %d", + win->remote_host, win->remote_type, paddedlen)); + + /* If this packet is for the current active connection we will + parse the packet right away to get it quickly on the screen. + Otherwise, it will be parsed with a timeout as the data is + for inactive window (which might not be visible at all). */ + if (SILC_CLIENT_IS_CURRENT_WIN(client, win)) { + /* Parse it real soon */ + silc_task_register(client->timeout_queue, fd, + silc_client_packet_parse, + (void *)packet, 0, 1, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + } else { + /* Parse the packet with timeout */ + silc_task_register(client->timeout_queue, fd, + silc_client_packet_parse, + (void *)packet, 0, 200000, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + } + + /* Pull the packet from inbuf thus we'll get the next one + in the inbuf. */ + silc_buffer_pull(sock->inbuf, paddedlen); + if (win->hmac) + silc_buffer_pull(sock->inbuf, mac_len); + } + silc_buffer_clear(sock->inbuf); + return; + } else { + /* Received one packet */ + + SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len), + sock->inbuf->data, sock->inbuf->len); + SILC_LOG_DEBUG(("Packet from server %s, " + "server type %d, packet length %d", + win->remote_host, win->remote_type, paddedlen)); + + packet = silc_calloc(1, sizeof(*packet)); + packet->client = client; + packet->sock = sock; + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_copy(sock->inbuf); + silc_buffer_clear(sock->inbuf); + + /* If this packet is for the current active connection we will + parse the packet right away to get it quickly on the screen. + Otherwise, it will be parsed with a timeout as the data is + for inactive window (which might not be visible at all). */ + if (SILC_CLIENT_IS_CURRENT_WIN(client, win)) { + /* Parse it real soon */ + silc_task_register(client->timeout_queue, fd, + silc_client_packet_parse, + (void *)packet, 0, 1, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } else { + /* Parse the packet with timeout */ + silc_task_register(client->timeout_queue, fd, + silc_client_packet_parse, + (void *)packet, 0, 200000, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } + } + } + } + + SILC_LOG_ERROR(("Weird, nothing happened - ignoring")); +} + +/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called + after packet has been totally decrypted and parsed. */ + +static int silc_client_packet_check_mac(SilcClient client, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + + /* Check MAC */ + if (win->hmac) { + int headlen = buffer->data - buffer->head, mac_len; + unsigned char *packet_mac, mac[32]; + + SILC_LOG_DEBUG(("Verifying MAC")); + + mac_len = win->hmac->hash->hash->hash_len; + + silc_buffer_push(buffer, headlen); + + /* Take mac from packet */ + packet_mac = buffer->tail; + + /* Make MAC and compare */ + memset(mac, 0, sizeof(mac)); + silc_hmac_make_with_key(win->hmac, + buffer->data, buffer->len, + win->hmac_key, win->hmac_key_len, mac); +#if 0 + SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len); + SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len); +#endif + if (memcmp(mac, packet_mac, mac_len)) { + SILC_LOG_DEBUG(("MAC failed")); + return FALSE; + } + + SILC_LOG_DEBUG(("MAC is Ok")); + memset(mac, 0, sizeof(mac)); + + silc_buffer_pull(buffer, headlen); + } + + return TRUE; +} + +/* Decrypts rest of the packet (after decrypting just the SILC header). + After calling this function the packet is ready to be parsed by calling + silc_packet_parse. */ + +static int silc_client_packet_decrypt_rest(SilcClient client, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + unsigned int mac_len = 0; + + /* Decrypt */ + if (win && win->receive_key) { + + /* Pull MAC from packet before decryption */ + if (win->hmac) { + mac_len = win->hmac->hash->hash->hash_len; + if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) { + silc_buffer_push_tail(buffer, mac_len); + } else { + SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped")); + return FALSE; + } + } + + SILC_LOG_DEBUG(("Decrypting rest of the packet")); + + /* Decrypt rest of the packet */ + silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + silc_packet_decrypt(win->receive_key, buffer, buffer->len); + silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + + SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len), + buffer->data, buffer->len); + } + + return TRUE; +} + +/* Decrypts rest of the SILC Packet header that has been decrypted partly + already. This decrypts the padding of the packet also. After calling + this function the packet is ready to be parsed by calling function + silc_packet_parse. This is used in special packet reception. */ + +static int silc_client_packet_decrypt_rest_special(SilcClient client, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + unsigned int mac_len = 0; + + /* Decrypt rest of the header plus padding */ + if (win && win->receive_key) { + unsigned short truelen, len1, len2, padlen; + + /* Pull MAC from packet before decryption */ + if (win->hmac) { + mac_len = win->hmac->hash->hash->hash_len; + if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) { + silc_buffer_push_tail(buffer, mac_len); + } else { + SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped")); + return FALSE; + } + } + + SILC_LOG_DEBUG(("Decrypting rest of the header")); + + SILC_GET16_MSB(len1, &buffer->data[4]); + SILC_GET16_MSB(len2, &buffer->data[6]); + + truelen = SILC_PACKET_HEADER_LEN + len1 + len2; + padlen = SILC_PACKET_PADLEN(truelen); + len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2); + + silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + SILC_LOG_HEXDUMP(("XXX"), buffer->data, buffer->len); + silc_packet_decrypt(win->receive_key, buffer, len1); + silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + SILC_LOG_HEXDUMP(("XXX"), buffer->data, buffer->len); + } + + return TRUE; +} + +/* Parses whole packet, received earlier. */ + +SILC_TASK_CALLBACK(silc_client_packet_parse) +{ + SilcClientInternalPacket *packet = (SilcClientInternalPacket *)context; + SilcBuffer buffer = packet->packetdata->buffer; + SilcClient client = packet->client; + SilcSocketConnection sock = packet->sock; + SilcClientWindow win = (SilcClientWindow)sock->user_data; + int ret; + + SILC_LOG_DEBUG(("Start")); + + /* Decrypt start of the packet header */ + if (win && win->receive_key) + silc_packet_decrypt(win->receive_key, buffer, SILC_PACKET_MIN_HEADER_LEN); + + /* If the packet type is not any special type lets decrypt rest + of the packet here. */ + if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE && + buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) { + normal: + /* Normal packet, decrypt rest of the packet */ + if (!silc_client_packet_decrypt_rest(client, sock, buffer)) + goto out; + + /* Parse the packet. Packet type is returned. */ + ret = silc_packet_parse(packet->packetdata); + if (ret == SILC_PACKET_NONE) + goto out; + + /* Check MAC */ + if (!silc_client_packet_check_mac(client, sock, buffer)) + goto out; + } else { + /* If private message key is not set for private message it is + handled as normal packet. Go back up. */ + if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE && + !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) + goto normal; + + /* Packet requires special handling, decrypt rest of the header. + This only decrypts. This does not do any MAC checking, it must + be done individually later when doing the special processing. */ + silc_client_packet_decrypt_rest_special(client, sock, buffer); + + /* Parse the packet header in special way as this is "special" + packet type. */ + ret = silc_packet_parse_special(packet->packetdata); + if (ret == SILC_PACKET_NONE) + goto out; + } + + /* Parse the incoming packet type */ + silc_client_packet_parse_type(client, sock, packet->packetdata); + + out: + silc_buffer_clear(packet->packetdata->buffer); + silc_free(packet->packetdata); + silc_free(packet); +} + +/* Parses the packet type and calls what ever routines the packet type + requires. This is done for all incoming packets. */ + +void silc_client_packet_parse_type(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcPacketType type = packet->type; + + SILC_LOG_DEBUG(("Parsing packet type %d", type)); + + /* Parse the packet type */ + switch(type) { + case SILC_PACKET_DISCONNECT: + silc_client_disconnected_by_server(client, sock, buffer); + break; + case SILC_PACKET_SUCCESS: + /* + * Success received for something. For now we can have only + * one protocol for connection executing at once hence this + * success message is for whatever protocol is executing currently. + */ + if (sock->protocol) { + sock->protocol->execute(client->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); + } + break; + case SILC_PACKET_FAILURE: + /* + * Failure received for some protocol. Set the protocol state to + * error and call the protocol callback. This fill cause error on + * protocol and it will call the final callback. + */ + if (sock->protocol) { + sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; + sock->protocol->execute(client->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); + } + break; + case SILC_PACKET_REJECT: + break; + + case SILC_PACKET_NOTIFY: + /* + * Received notify message + */ + silc_client_notify_by_server(client, sock, buffer); + break; + + case SILC_PACKET_ERROR: + /* + * Received error message + */ + silc_client_error_by_server(client, sock, buffer); + break; + + case SILC_PACKET_CHANNEL_MESSAGE: + /* + * Received message to (from, actually) a channel + */ + silc_client_channel_message(client, sock, packet); + break; + case SILC_PACKET_CHANNEL_KEY: + /* + * Received key for a channel. By receiving this key the client will be + * able to talk to the channel it has just joined. This can also be + * a new key for existing channel as keys expire peridiocally. + */ + silc_client_receive_channel_key(client, sock, buffer); + break; + + case SILC_PACKET_PRIVATE_MESSAGE: + /* + * Received private message + */ + { + SilcClientCommandReplyContext ctx; + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->sock = sock; + ctx->context = buffer; /* kludge */ + silc_client_command_reply_msg((void *)ctx); + } + break; + case SILC_PACKET_PRIVATE_MESSAGE_KEY: + /* + * Received private message key + */ + break; + + case SILC_PACKET_COMMAND_REPLY: + /* + * Recived reply for a command + */ + silc_client_command_reply_process(client, sock, buffer); + break; + + case SILC_PACKET_KEY_EXCHANGE: + if (sock->protocol) { + SilcClientKEInternalContext *proto_ctx = + (SilcClientKEInternalContext *)sock->protocol->context; + + proto_ctx->packet = buffer; + + /* Let the protocol handle the packet */ + sock->protocol->execute(client->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); + } else { + SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange " + "protocol active, packet dropped.")); + + /* XXX Trigger KE protocol?? Rekey actually! */ + } + break; + + case SILC_PACKET_KEY_EXCHANGE_1: + if (sock->protocol) { + + } else { + SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange " + "protocol active, packet dropped.")); + } + break; + case SILC_PACKET_KEY_EXCHANGE_2: + if (sock->protocol) { + SilcClientKEInternalContext *proto_ctx = + (SilcClientKEInternalContext *)sock->protocol->context; + + if (proto_ctx->packet) + silc_buffer_free(proto_ctx->packet); + + proto_ctx->packet = buffer; + + /* Let the protocol handle the packet */ + sock->protocol->execute(client->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); + } else { + SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange " + "protocol active, packet dropped.")); + } + break; + + case SILC_PACKET_NEW_ID: + { + /* + * Received new ID from server. This packet is received at + * the connection to the server. New ID is also received when + * user changes nickname but in that case the new ID is received + * as command reply and not as this packet type. + */ + unsigned char *id_string; + unsigned short id_type; + + silc_buffer_unformat(buffer, + SILC_STR_UI_SHORT(&id_type), + SILC_STR_UI16_STRING_ALLOC(&id_string), + SILC_STR_END); + + if ((SilcIdType)id_type != SILC_ID_CLIENT) + break; + + silc_client_receive_new_id(client, sock, id_string); + silc_free(id_string); + break; + } + + default: + SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type)); + break; + } +} + +/* Internal routine that sends packet or marks packet to be sent. This + is used directly only in special cases. Normal cases should use + silc_server_packet_send. Returns < 0 on error. */ + +static int silc_client_packet_send_real(SilcClient client, + SilcSocketConnection sock, + int force_send) +{ + /* Send now if forced to do so */ + if (force_send == TRUE) { + int ret; + SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately")); + ret = silc_packet_write(sock->sock, sock->outbuf); + + if (ret == -1) + SILC_LOG_ERROR(("Packet dropped")); + if (ret != -2) + return ret; + + SILC_LOG_DEBUG(("Could not force the send, packet put to queue")); + } + + SILC_LOG_DEBUG(("Packet in queue")); + + /* Mark that there is some outgoing data available for this connection. + This call sets the connection both for input and output (the input + is set always and this call keeps the input setting, actually). + Actual data sending is performed by silc_client_packet_process. */ + SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(sock->sock); + + /* Mark to socket that data is pending in outgoing buffer. This flag + is needed if new data is added to the buffer before the earlier + put data is sent to the network. */ + SILC_SET_OUTBUF_PENDING(sock); + + return 0; +} + +/* Prepare outgoing data buffer for packet sending. */ + +static void silc_client_packet_send_prepare(SilcClient client, + SilcSocketConnection sock, + unsigned int header_len, + unsigned int padlen, + unsigned int data_len) +{ + int totlen, oldlen; + + totlen = header_len + padlen + data_len; + + /* 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")); + + sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE); + silc_buffer_pull_tail(sock->outbuf, totlen); + silc_buffer_pull(sock->outbuf, header_len + padlen); + } else { + if (SILC_IS_OUTBUF_PENDING(sock)) { + /* There is some pending data in the buffer. */ + + if ((sock->outbuf->end - sock->outbuf->tail) < data_len) { + SILC_LOG_DEBUG(("Reallocating outgoing data buffer")); + /* XXX: not done yet */ + } + oldlen = sock->outbuf->len; + silc_buffer_pull_tail(sock->outbuf, totlen); + silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen); + } else { + /* Buffer is free for use */ + silc_buffer_clear(sock->outbuf); + silc_buffer_pull_tail(sock->outbuf, totlen); + silc_buffer_pull(sock->outbuf, header_len + padlen); + } + } +} + +/* Sends packet. This doesn't actually send the packet instead it assembles + it and marks it to be sent. However, if force_send is TRUE the packet + is sent immediately. if dst_id, cipher and hmac are NULL those parameters + will be derived from sock argument. Otherwise the valid arguments sent + are used. */ + +void silc_client_packet_send(SilcClient client, + SilcSocketConnection sock, + SilcPacketType type, + void *dst_id, + SilcIdType dst_id_type, + SilcCipher cipher, + SilcHmac hmac, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + SilcPacketContext packetdata; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + + SILC_LOG_DEBUG(("Sending packet, type %d", type)); + + /* Get data used in the packet sending, keys and stuff */ + if ((!cipher || !hmac || !dst_id) && sock->user_data) { + if (!cipher && ((SilcClientWindow)sock->user_data)->send_key) + cipher = ((SilcClientWindow)sock->user_data)->send_key; + if (!hmac && ((SilcClientWindow)sock->user_data)->hmac) { + hmac = ((SilcClientWindow)sock->user_data)->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = ((SilcClientWindow)sock->user_data)->hmac_key; + hmac_key_len = ((SilcClientWindow)sock->user_data)->hmac_key_len; + } + if (!dst_id && ((SilcClientWindow)sock->user_data)->remote_id) { + dst_id = ((SilcClientWindow)sock->user_data)->remote_id; + dst_id_type = SILC_ID_SERVER; + } + } + + /* Set the packet context pointers */ + packetdata.flags = 0; + packetdata.type = type; + if (((SilcClientWindow)sock->user_data)->local_id_data) + packetdata.src_id = ((SilcClientWindow)sock->user_data)->local_id_data; + else + packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char)); + packetdata.src_id_len = SILC_ID_CLIENT_LEN; + packetdata.src_id_type = SILC_ID_CLIENT; + if (dst_id) { + packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type); + packetdata.dst_id_len = silc_id_get_len(dst_id_type); + packetdata.dst_id_type = dst_id_type; + } else { + packetdata.dst_id = NULL; + packetdata.dst_id_len = 0; + packetdata.dst_id_type = SILC_ID_NONE; + } + packetdata.rng = client->rng; + packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen); + + /* Prepare outgoing data buffer for packet sending */ + silc_client_packet_send_prepare(client, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + data_len); + + SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len)); + + packetdata.buffer = sock->outbuf; + + /* Put the data to the buffer */ + if (data && data_len) + silc_buffer_put(sock->outbuf, data, data_len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet */ + if (hmac) { + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + } + + /* Encrypt the packet */ + if (cipher) + silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len); + + /* Pull MAC into the visible data area */ + if (hmac) + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_client_packet_send_real(client, sock, force_send); +} + +/* Sends packet to a channel. Packet to channel is always encrypted + differently from "normal" packets. SILC header of the packet is + encrypted with the next receiver's key and the rest of the packet is + encrypted with the channel specific key. Padding and HMAC is computed + with the next receiver's key. */ + +void silc_client_packet_send_to_channel(SilcClient client, + SilcSocketConnection sock, + SilcChannelEntry channel, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + int i; + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcBuffer payload; + SilcPacketContext packetdata; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + unsigned char *id_string; + SilcCipher cipher; + SilcHmac hmac; + + SILC_LOG_DEBUG(("Sending packet to channel")); + + if (!channel || !channel->key) { + silc_say(client, "Cannot talk to channel: key does not exist"); + return; + } + + /* Generate IV */ + if (!channel->iv) + for (i = 0; i < 16; i++) + channel->iv[i] = silc_rng_get_byte(client->rng); + else + silc_hash_make(client->md5hash, channel->iv, 16, channel->iv); + + /* Encode the channel payload */ + payload = silc_channel_encode_payload(strlen(win->nickname), win->nickname, + data_len, data, 16, channel->iv, + client->rng); + if (!payload) { + silc_say(client, + "Error: Could not create packet to be sent to the channel"); + return; + } + + /* Get data used in packet header encryption, keys and stuff. Rest + of the packet (the payload) is, however, encrypted with the + specified channel key. */ + cipher = win->send_key; + hmac = win->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = win->hmac_key; + hmac_key_len = win->hmac_key_len; + id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + + /* Set the packet context pointers. The destination ID is always + the Channel ID of the channel. Server and router will handle the + distribution of the packet. */ + packetdata.flags = 0; + packetdata.type = SILC_PACKET_CHANNEL_MESSAGE; + packetdata.src_id = win->local_id_data; + packetdata.src_id_len = SILC_ID_CLIENT_LEN; + packetdata.src_id_type = SILC_ID_CLIENT; + packetdata.dst_id = id_string; + packetdata.dst_id_len = SILC_ID_CHANNEL_LEN; + packetdata.dst_id_type = SILC_ID_CHANNEL; + packetdata.rng = client->rng; + packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len)); + + /* Prepare outgoing data buffer for packet sending */ + silc_client_packet_send_prepare(client, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + payload->len); + + packetdata.buffer = sock->outbuf; + + /* Encrypt payload of the packet. This is encrypted with the channel key. */ + channel->channel_key->cipher->encrypt(channel->channel_key->context, + payload->data, payload->data, + payload->len - 16, /* -IV_LEN */ + channel->iv); + + SILC_LOG_HEXDUMP(("XXX"), payload->data, payload->len); + + /* Put the actual encrypted payload data into the buffer. */ + silc_buffer_put(sock->outbuf, payload->data, payload->len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + SILC_LOG_HEXDUMP(("XXX"), sock->outbuf->data, sock->outbuf->len); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with our server. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_client_packet_send_real(client, sock, force_send); + silc_buffer_free(payload); + silc_free(id_string); +} + +/* Sends private message to remote client. If private message key has + not been set with this client then the message will be encrypted using + normal session keys. Private messages are special packets in SILC + network hence we need this own function for them. This is similiar + to silc_client_packet_send_to_channel except that we send private + message. */ + +void silc_client_packet_send_private_message(SilcClient client, + SilcSocketConnection sock, + SilcClientEntry client_entry, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcBuffer buffer; + SilcPacketContext packetdata; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + unsigned int nick_len; + SilcCipher cipher; + SilcHmac hmac; + + SILC_LOG_DEBUG(("Sending private message")); + + /* Create private message payload */ + nick_len = strlen(client->current_win->nickname); + buffer = silc_buffer_alloc(2 + nick_len + data_len); + silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer)); + silc_buffer_format(buffer, + SILC_STR_UI_SHORT(nick_len), + SILC_STR_UI_XNSTRING(client->current_win->nickname, + nick_len), + SILC_STR_UI_XNSTRING(data, data_len), + SILC_STR_END); + + /* If we don't have private message specific key then private messages + are just as any normal packet thus call normal packet sending. If + the key exist then the encryption process is a bit different and + will be done in the rest of this function. */ + if (!client_entry->send_key) { + silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE, + client_entry->id, SILC_ID_CLIENT, NULL, NULL, + buffer->data, buffer->len, force_send); + goto out; + } + + /* We have private message specific key */ + + /* Get data used in the encryption */ + cipher = client_entry->send_key; + hmac = win->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = win->hmac_key; + hmac_key_len = win->hmac_key_len; + + /* Set the packet context pointers. */ + packetdata.flags = 0; + packetdata.type = SILC_PACKET_PRIVATE_MESSAGE; + packetdata.src_id = win->local_id_data; + packetdata.src_id_len = SILC_ID_CLIENT_LEN; + packetdata.src_id_type = SILC_ID_CLIENT; + if (client_entry) + packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT); + else + packetdata.dst_id = win->local_id_data; + packetdata.dst_id_len = SILC_ID_CLIENT_LEN; + packetdata.dst_id_type = SILC_ID_CLIENT; + packetdata.rng = client->rng; + packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len)); + + /* Prepare outgoing data buffer for packet sending */ + silc_client_packet_send_prepare(client, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + buffer->len); + + packetdata.buffer = sock->outbuf; + + /* Encrypt payload of the packet. Encrypt with private message specific + key if it exist, otherwise with session key. */ + cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data, + buffer->len, cipher->iv); + + /* Put the actual encrypted payload data into the buffer. */ + silc_buffer_put(sock->outbuf, buffer->data, buffer->len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + SILC_LOG_HEXDUMP(("XXX"), sock->outbuf->data, sock->outbuf->len); + + /* Encrypt the header and padding of the packet. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_client_packet_send_real(client, sock, force_send); + silc_free(packetdata.dst_id); + + out: + silc_free(buffer); +} + +/* Closes connection to remote end. Free's all allocated data except + for some information such as nickname etc. that are valid at all time. */ + +void silc_client_close_connection(SilcClient client, + SilcSocketConnection sock) +{ + SilcClientWindow win; + int i; + + /* We won't listen for this connection anymore */ + silc_schedule_unset_listen_fd(sock->sock); + + /* Unregister all tasks */ + silc_task_unregister_by_fd(client->io_queue, sock->sock); + silc_task_unregister_by_fd(client->timeout_queue, sock->sock); + + /* Close the actual connection */ + silc_net_close_connection(sock->sock); + + silc_say(client, "Closed connection to host %s", sock->hostname ? + sock->hostname : sock->ip); + + /* Free everything */ + if (sock->user_data) { + win = (SilcClientWindow)sock->user_data; + + /* Clear ID caches */ + for (i = 0; i < 96; i++) + silc_idcache_del_all(&win->client_id_cache[i], + win->client_id_cache_count[i]); + for (i = 0; i < 96; i++) + silc_idcache_del_all(&win->channel_id_cache[i], + win->channel_id_cache_count[i]); + + /* Free data */ + if (win->remote_host) + silc_free(win->remote_host); + if (win->local_id) + silc_free(win->local_id); + if (win->local_id_data) + silc_free(win->local_id_data); + if (win->send_key) + silc_cipher_free(win->send_key); + if (win->receive_key) + silc_cipher_free(win->receive_key); + if (win->public_key) + silc_pkcs_free(win->public_key); + if (win->hmac) + silc_hmac_free(win->hmac); + if (win->hmac_key) { + memset(win->hmac_key, 0, win->hmac_key_len); + silc_free(win->hmac_key); + } + + win->sock = NULL; + win->remote_port = 0; + win->remote_type = 0; + win->send_key = NULL; + win->receive_key = NULL; + win->public_key = NULL; + win->hmac = NULL; + win->hmac_key = NULL; + win->hmac_key_len = 0; + win->local_id = NULL; + win->local_id_data = NULL; + win->remote_host = NULL; + } + + if (sock->protocol) { + silc_protocol_free(sock->protocol); + sock->protocol = NULL; + } + silc_socket_free(sock); +} + +/* Called when we receive disconnection packet from server. This + closes our end properly and displays the reason of the disconnection + on the screen. */ + +void silc_client_disconnected_by_server(SilcClient client, + SilcSocketConnection sock, + SilcBuffer message) +{ + char *msg; + + SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock)); + + msg = silc_calloc(message->len + 1, sizeof(char)); + memcpy(msg, message->data, message->len); + silc_say(client, msg); + silc_free(msg); + + SILC_SET_DISCONNECTED(sock); + silc_client_close_connection(client, sock); +} + +/* Received error message from server. Display it on the screen. + We don't take any action what so ever of the error message. */ + +void silc_client_error_by_server(SilcClient client, + SilcSocketConnection sock, + SilcBuffer message) +{ + char *msg; + + msg = silc_calloc(message->len + 1, sizeof(char)); + memcpy(msg, message->data, message->len); + silc_say(client, msg); + silc_free(msg); +} + +/* Received notify message from server */ + +void silc_client_notify_by_server(SilcClient client, + SilcSocketConnection sock, + SilcBuffer message) +{ + char *msg; + + msg = silc_calloc(message->len + 1, sizeof(char)); + memcpy(msg, message->data, message->len); + silc_say(client, msg); + silc_free(msg); +} + +/* Processes the received new Client ID from server. Old Client ID is + deleted from cache and new one is added. */ + +void silc_client_receive_new_id(SilcClient client, + SilcSocketConnection sock, + unsigned char *id_string) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + char *nickname = win->nickname; + +#define CIDC(x) win->client_id_cache[(x) - 32] +#define CIDCC(x) win->client_id_cache_count[(x) - 32] + + /* Delete old ID from ID cache */ + silc_idcache_del_by_id(CIDC(nickname[0]), CIDCC(nickname[0]), + SILC_ID_CLIENT, win->local_id); + + /* Save the new ID */ + if (win->local_id) + silc_free(win->local_id); + win->local_id = silc_id_str2id(id_string, SILC_ID_CLIENT); + if (win->local_id_data) + silc_free(win->local_id_data); + win->local_id_data = + silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char)); + memcpy(win->local_id_data, id_string, SILC_ID_CLIENT_LEN); + win->local_id_data_len = SILC_ID_CLIENT_LEN; + if (!win->local_entry) + win->local_entry = silc_calloc(1, sizeof(*win->local_entry)); + win->local_entry->nickname = win->nickname; + win->local_entry->id = win->local_id; + + /* Put it to the ID cache */ + CIDCC(nickname[0]) = silc_idcache_add(&CIDC(nickname[0]), + CIDCC(nickname[0]), + win->nickname, SILC_ID_CLIENT, + win->local_id, + (void *)win->local_entry); +#undef CIDC +#undef CIDCC +} + +/* Processed received Channel ID for a channel. This is called when client + joins to channel and server replies with channel ID. The ID is cached. */ + +void silc_client_new_channel_id(SilcClient client, + SilcSocketConnection sock, + char *channel_name, + unsigned char *id_string) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcChannelID *id; + SilcChannelEntry channel; + + SILC_LOG_DEBUG(("New channel ID")); + +#define CIDC(x) win->channel_id_cache[(x) - 32] +#define CIDCC(x) win->channel_id_cache_count[(x) - 32] + + id = silc_id_str2id(id_string, SILC_ID_CHANNEL); + channel = silc_calloc(1, sizeof(*channel)); + channel->channel_name = channel_name; + channel->id = id; + win->current_channel = channel; + + /* Put it to the ID cache */ + CIDCC(channel_name[0]) = silc_idcache_add(&CIDC(channel_name[0]), + CIDCC(channel_name[0]), + channel_name, SILC_ID_CHANNEL, + id, (void *)channel); +#undef CIDC +#undef CIDCC +} + +/* Processes received key for channel. The received key will be used + to protect the traffic on the channel for now on. Client must receive + the key to the channel before talking on the channel is possible. + This is the key that server has generated, this is not the channel + private key, it is entirely local setting. */ + +void silc_client_receive_channel_key(SilcClient client, + SilcSocketConnection sock, + SilcBuffer packet) +{ + int i; + unsigned char *id_string, *key, *cipher; + unsigned int key_len; + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcChannelID *id; + SilcIDCache *id_cache = NULL; + SilcChannelEntry channel; + SilcChannelKeyPayload payload; + + SILC_LOG_DEBUG(("Received key for channel")); + +#define CIDC(x) win->channel_id_cache[(x)] +#define CIDCC(x) win->channel_id_cache_count[(x)] + + payload = silc_channel_key_parse_payload(packet); + if (!payload) + return; + + id_string = silc_channel_key_get_id(payload, NULL); + if (!id_string) { + silc_channel_key_free_payload(payload); + return; + } + id = silc_id_str2id(id_string, SILC_ID_CHANNEL); + + /* Find channel. XXX: This is bad and slow. */ + for (i = 0; i < 96; i++) { + if (CIDC(i) == NULL) + continue; + if (silc_idcache_find_by_id(CIDC(i), CIDCC(i), (void *)id, + SILC_ID_CHANNEL, &id_cache)) + break; + } + + if (!id_cache) + goto out; + + /* Save the key */ + key = silc_channel_key_get_key(payload, &key_len); + cipher = silc_channel_key_get_cipher(payload, NULL); + + channel = (SilcChannelEntry)id_cache->context; + channel->key_len = key_len; + channel->key = silc_calloc(key_len, sizeof(*channel->key)); + memcpy(channel->key, key, key_len); + + silc_cipher_alloc(cipher, &channel->channel_key); + if (!channel->channel_key) { + silc_say(client, "Cannot talk to channel: unsupported cipher %s", cipher); + goto out; + } + channel->channel_key->cipher->set_key(channel->channel_key->context, + key, key_len); + + /* Client is now joined to the channel */ + channel->on_channel = TRUE; + + out: + silc_free(id); + silc_channel_key_free_payload(payload); +#undef CIDC +#undef CIDCC +} + +/* Process received message to a channel (or from a channel, really). This + decrypts the channel message with channel specific key and parses the + channel payload. Finally it displays the message on the screen. */ + +void silc_client_channel_message(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + int i; + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcBuffer buffer = packet->buffer; + SilcChannelPayload payload = NULL; + SilcChannelID *id = NULL; + SilcChannelEntry channel; + SilcIDCache *id_cache = NULL; + +#define CIDC(x) win->channel_id_cache[(x)] +#define CIDCC(x) win->channel_id_cache_count[(x)] + + /* Sanity checks */ + if (packet->dst_id_type != SILC_ID_CHANNEL) + goto out; + + id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL); + + /* Find the channel entry from channels on this window */ + for (i = 0; i < 96; i++) { + if (CIDC(i) == NULL) + continue; + if (silc_idcache_find_by_id(CIDC(i), CIDCC(i), (void *)id, + SILC_ID_CHANNEL, &id_cache)) + break; + } + + if (!id_cache) + goto out; + + channel = (SilcChannelEntry)id_cache->context; + + /* Decrypt the channel message payload. Push the IV out of the way, + since it is not encrypted (after pushing buffer->tail has the IV). */ + silc_buffer_push_tail(buffer, 16); + channel->channel_key->cipher->decrypt(channel->channel_key->context, + buffer->data, buffer->data, + buffer->len, buffer->tail); + silc_buffer_pull_tail(buffer, 16); + + /* Parse the channel message payload */ + payload = silc_channel_parse_payload(buffer); + if (!payload) + goto out; + + /* Display the message on screen */ + if (packet->src_id_type == SILC_ID_CLIENT) + /* Message from client */ + silc_print(client, "<%s> %s", silc_channel_get_nickname(payload, NULL), + silc_channel_get_data(payload, NULL)); + else + /* Message from server */ + silc_say(client, "%s", silc_channel_get_data(payload, NULL)); + + out: + if (id) + silc_free(id); + if (payload) + silc_channel_free_payload(payload); +#undef CIDC +#undef CIDCC +} diff --git a/apps/silc/client.h b/apps/silc/client.h new file mode 100644 index 00000000..661bd83a --- /dev/null +++ b/apps/silc/client.h @@ -0,0 +1,249 @@ +/* + + client.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef CLIENT_H +#define CLIENT_H + +/* Window structure used in client to associate all the important + connection (window) specific data to this structure. How the window + actually appears on the screen in handeled by the silc_screen* + routines in screen.c. */ +typedef struct { + /* + * Local data + */ + char *nickname; + + /* Local client ID for this connection */ + SilcClientID *local_id; + + /* Decoded local ID so that the above defined ID would not have + to be decoded for every packet. */ + unsigned char *local_id_data; + unsigned int local_id_data_len; + + /* Own client entry. */ + SilcClientEntry local_entry; + + /* + * Remote data + */ + char *remote_host; + int remote_port; + int remote_type; + + /* Remote client ID for this connection */ + SilcClientID *remote_id; + + /* Remote local ID so that the above defined ID would not have + to be decoded for every packet. */ + unsigned char *remote_id_data; + unsigned int remote_id_data_len; + + /* + * Common data + */ + /* Keys */ + SilcCipher send_key; + SilcCipher receive_key; + SilcPKCS public_key; + SilcHmac hmac; + unsigned char *hmac_key; + unsigned int hmac_key_len; + + /* Client ID and Channel ID cache. Messages transmitted in SILC network + are done using different unique ID's. These are the cache for + thoses ID's used in the communication. */ + SilcIDCache *client_id_cache[96]; + unsigned int client_id_cache_count[96]; + SilcIDCache *channel_id_cache[96]; + unsigned int channel_id_cache_count[96]; + SilcIDCache *server_id_cache; + unsigned int server_id_cache_count; + + /* Current channel on window. All channel's are saved (allocated) into + the cache entries. */ + SilcChannelEntry current_channel; + + /* Socket connection object for this connection (window). This + object will have a back-pointer to this window object for fast + referencing (sock->user_data). */ + SilcSocketConnection sock; + + /* The actual physical screen. This data is handled by the + screen handling routines. */ + void *screen; +} *SilcClientWindow; + +typedef struct { + char *username; + char *realname; + + /* SILC client task queues */ + SilcTaskQueue io_queue; + SilcTaskQueue timeout_queue; + SilcTaskQueue generic_queue; + + /* Input buffer that holds the characters user types. This is + used only to store the typed chars for a while. */ + SilcBuffer input_buffer; + + /* Table of windows in client. All the data, including connection + specific data, is saved in here. */ + SilcClientWindow *windows; + unsigned int windows_count; + + /* Currently active window. This is pointer to the window table + defined above. This must never be free'd directly. */ + SilcClientWindow current_win; + + /* The SILC client screen object */ + SilcScreen screen; + + /* Generic cipher and hash objects */ + SilcCipher none_cipher; + SilcHash md5hash; + SilcHash sha1hash; + SilcHmac md5hmac; + SilcHmac sha1hmac; + + /* Configuration object */ + SilcClientConfig config; + + /* Random Number Generator */ + SilcRng rng; + +#ifdef SILC_SIM + /* SIM (SILC Module) table */ + SilcSimContext **sim; + unsigned int sim_count; +#endif +} SilcClientObject; + +typedef SilcClientObject *SilcClient; + +/* Macros */ + +#ifndef CTRL +#define CTRL(x) ((x) & 0x1f) /* Ctrl+x */ +#endif + +/* Registers generic task for file descriptor for reading from network and + writing to network. As being generic task the actual task is allocated + only once and after that the same task applies to all registered fd's. */ +#define SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd) \ +do { \ + SilcTask tmptask = silc_task_register(client->generic_queue, (fd), \ + silc_client_packet_process, \ + context, 0, 0, \ + SILC_TASK_GENERIC, \ + SILC_TASK_PRI_NORMAL); \ + silc_task_set_iotype(tmptask, SILC_TASK_WRITE); \ +} while(0) + +#define SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd) \ +do { \ + silc_schedule_set_listen_fd((fd), (1L << SILC_TASK_READ)); \ +} while(0) \ + +#define SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(fd) \ +do { \ + silc_schedule_set_listen_fd((fd), ((1L << SILC_TASK_READ) | \ + (1L << SILC_TASK_WRITE))); \ +} while(0) + +/* Finds socket connection object by file descriptor */ +#define SILC_CLIENT_GET_SOCK(__x, __fd, __sock) \ +do { \ + int __i; \ + \ + for (__i = 0; __i < (__x)->windows_count; __i++) \ + if ((__x)->windows[__i]->sock->sock == (__fd)) \ + break; \ + \ + if (__i >= (__x)->windows_count) \ + (__sock) = NULL; \ + (__sock) = (__x)->windows[__i]->sock; \ +} while(0) + +/* Returns TRUE if windows is currently active window */ +#define SILC_CLIENT_IS_CURRENT_WIN(__x, __win) ((__x)->current_win == (__win)) + +/* Prototypes */ +int silc_client_alloc(SilcClient *new_client); +void silc_client_free(SilcClient client); +int silc_client_init(SilcClient client); +void silc_client_stop(SilcClient client); +void silc_client_run(SilcClient client); +void silc_client_parse_command_line(unsigned char *buffer, + unsigned char ***parsed, + unsigned int **parsed_lens, + unsigned int **parsed_types, + unsigned int *parsed_num, + unsigned int max_args); +int silc_client_connect_to_server(SilcClient client, int port, + char *host); +void silc_client_packet_send(SilcClient client, + SilcSocketConnection sock, + SilcPacketType type, + void *dst_id, + SilcIdType dst_id_type, + SilcCipher cipher, + SilcHmac hmac, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_client_packet_send_to_channel(SilcClient client, + SilcSocketConnection sock, + SilcChannelEntry channel, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_client_packet_send_private_message(SilcClient client, + SilcSocketConnection sock, + SilcClientEntry client_entry, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_client_close_connection(SilcClient client, + SilcSocketConnection sock); +void silc_client_disconnected_by_server(SilcClient client, + SilcSocketConnection sock, + SilcBuffer message); +void silc_client_error_by_server(SilcClient client, + SilcSocketConnection sock, + SilcBuffer message); +void silc_client_notify_by_server(SilcClient client, + SilcSocketConnection sock, + SilcBuffer message); +void silc_client_receive_new_id(SilcClient client, + SilcSocketConnection sock, + unsigned char *id_string); +void silc_client_new_channel_id(SilcClient client, + SilcSocketConnection sock, + char *channel_name, + unsigned char *id_string); +void silc_client_receive_channel_key(SilcClient client, + SilcSocketConnection sock, + SilcBuffer packet); +void silc_client_channel_message(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet); +#endif diff --git a/apps/silc/clientconfig.c b/apps/silc/clientconfig.c new file mode 100644 index 00000000..7cdd8154 --- /dev/null +++ b/apps/silc/clientconfig.c @@ -0,0 +1,754 @@ +/* + + serverconfig.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" +#include "clientconfig.h" + +/* + All possible configuration sections for SILC client. +*/ +SilcClientConfigSection silc_client_config_sections[] = { + { "[cipher]", + SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 }, + { "[pkcs]", + SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 2 }, + { "[hash]", + SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 }, + { "[connection]", + SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 }, + { "[commands]", + SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND, 0 }, + + { NULL, SILC_CLIENT_CONFIG_SECTION_TYPE_NONE, 0 } +}; + +/* Allocates a new configuration object, opens configuration file and + parses the file. The parsed data is returned to the newly allocated + configuration object. */ + +SilcClientConfig silc_client_config_alloc(char *filename) +{ + SilcClientConfig new; + SilcBuffer buffer; + SilcClientConfigParse config_parse; + + SILC_LOG_DEBUG(("Allocating new configuration object")); + + new = silc_calloc(1, sizeof(*new)); + if (!new) { + fprintf(stderr, "Could not allocate new configuration object"); + return NULL; + } + + new->filename = filename; + + /* Open configuration file and parse it */ + config_parse = NULL; + buffer = NULL; + silc_config_open(filename, &buffer); + if (!buffer) + goto fail; + if ((silc_client_config_parse(new, buffer, &config_parse)) == FALSE) + goto fail; + if ((silc_client_config_parse_lines(new, config_parse)) == FALSE) + goto fail; + + silc_free(buffer); + + return new; + + fail: + silc_free(new); + return NULL; +} + +/* Free's a configuration object. */ + +void silc_client_config_free(SilcClientConfig config) +{ + if (config) { + + silc_free(config); + } +} + +/* Parses the the buffer and returns the parsed lines into return_config + argument. The return_config argument doesn't have to be initialized + before calling this. It will be initialized during the parsing. The + buffer sent as argument can be safely free'd after this function has + succesfully returned. */ + +int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer, + SilcClientConfigParse *return_config) +{ + int i, begin; + unsigned int linenum; + char line[1024], *cp; + SilcClientConfigSection *cptr = NULL; + SilcClientConfigParse parse = *return_config, first = NULL; + + SILC_LOG_DEBUG(("Parsing configuration file")); + + begin = 0; + linenum = 0; + while((begin = silc_gets(line, sizeof(line), + buffer->data, buffer->len, begin)) != EOF) { + cp = line; + linenum++; + + /* Check for bad line */ + if (silc_check_line(cp)) + continue; + + /* Remove tabs and whitespaces from the line */ + if (strchr(cp, '\t')) { + i = 0; + while(strchr(cp + i, '\t')) { + *strchr(cp + i, '\t') = ' '; + i++; + } + } + for (i = 0; i < strlen(cp); i++) { + if (cp[i] != ' ') { + if (i) + cp++; + break; + } + cp++; + } + + /* Parse line */ + switch(cp[0]) { + case '[': + /* + * Start of a section + */ + + /* Remove new line sign */ + if (strchr(cp, '\n')) + *strchr(cp, '\n') = '\0'; + + /* Check for matching sections */ + for (cptr = silc_client_config_sections; cptr->section; cptr++) + if (!strcmp(cp, cptr->section)) + break; + + if (!cptr->section) { + fprintf(stderr, "%s:%d: Unknown section `%s'\n", + config->filename, linenum, cp); + return FALSE; + } + + break; + default: + /* + * Start of a configuration line + */ + + /* Handle config section */ + if (cptr->type != SILC_CLIENT_CONFIG_SECTION_TYPE_NONE) { + + if (strchr(cp, '\n')) + *strchr(cp, '\n') = ':'; + + if (parse == NULL) { + parse = silc_calloc(1, sizeof(*parse)); + parse->line = NULL; + parse->section = NULL; + parse->next = NULL; + parse->prev = NULL; + } else { + if (parse->next == NULL) { + parse->next = silc_calloc(1, sizeof(*parse->next)); + parse->next->line = NULL; + parse->next->section = NULL; + parse->next->next = NULL; + parse->next->prev = parse; + parse = parse->next; + } + } + + if (first == NULL) + first = parse; + + /* Add the line to parsing structure for further parsing. */ + if (parse) { + parse->section = cptr; + parse->line = silc_buffer_alloc(strlen(cp) + 1); + parse->linenum = linenum; + silc_buffer_pull_tail(parse->line, strlen(cp)); + silc_buffer_put(parse->line, cp, strlen(cp)); + } + } + break; + } + } + + /* Set the return_config argument to its first value so that further + parsing can be started from the first line. */ + *return_config = first; + + return TRUE; +} + +/* Parses the lines earlier read from configuration file. The config object + must not be initialized, it will be initialized in this function. The + parse_config argument is uninitialized automatically during this + function. */ + +int silc_client_config_parse_lines(SilcClientConfig config, + SilcClientConfigParse parse_config) +{ + int ret, check = FALSE; + char *tmp; + SilcClientConfigParse pc = parse_config; + SilcBuffer line; + + SILC_LOG_DEBUG(("Parsing configuration lines")); + + if (!config) + return FALSE; + + while(pc) { + check = FALSE; + line = pc->line; + + /* Get number of tokens in line (command section is handeled + specially and has no tokens at all). */ + ret = silc_config_check_num_token(line); + if (ret != pc->section->maxfields && + pc->section->type != SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND) { + /* Bad line */ + fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n", + config->filename, pc->linenum, ret, + pc->section->maxfields); + break; + } + + /* Parse the line */ + switch(pc->section->type) { + case SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER: + + if (!config->cipher) { + config->cipher = silc_calloc(1, sizeof(*config->cipher)); + config->cipher->next = NULL; + config->cipher->prev = NULL; + } else { + if (!config->cipher->next) { + config->cipher->next = + silc_calloc(1, sizeof(*config->cipher->next)); + config->cipher->next->next = NULL; + config->cipher->next->prev = config->cipher; + config->cipher = config->cipher->next; + } + } + + /* Get cipher name */ + ret = silc_config_get_token(line, &config->cipher->alg_name); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Cipher name not defined\n", + config->filename, pc->linenum); + break; + } + + /* Get module name */ + config->cipher->sim_name = NULL; + ret = silc_config_get_token(line, &config->cipher->sim_name); + if (ret < 0) + break; + + /* Get block length */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Cipher block length not defined\n", + config->filename, pc->linenum); + break; + } + config->cipher->block_len = atoi(tmp); + silc_free(tmp); + + /* Get key length */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Cipher key length not defined\n", + config->filename, pc->linenum); + break; + } + config->cipher->key_len = atoi(tmp); + silc_free(tmp); + + check = TRUE; + break; + + case SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS: + + if (!config->pkcs) { + config->pkcs = silc_calloc(1, sizeof(*config->pkcs)); + config->pkcs->next = NULL; + config->pkcs->prev = NULL; + } else { + if (!config->pkcs->next) { + config->pkcs->next = + silc_calloc(1, sizeof(*config->pkcs->next)); + config->pkcs->next->next = NULL; + config->pkcs->next->prev = config->pkcs; + config->pkcs = config->pkcs->next; + } + } + + /* Get PKCS name */ + ret = silc_config_get_token(line, &config->pkcs->alg_name); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: PKCS name not defined\n", + config->filename, pc->linenum); + break; + } + + /* Get key length */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: PKCS key length not defined\n", + config->filename, pc->linenum); + break; + } + config->pkcs->key_len = atoi(tmp); + silc_free(tmp); + + check = TRUE; + break; + + case SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION: + + if (!config->hash_func) { + config->hash_func = silc_calloc(1, sizeof(*config->hash_func)); + config->hash_func->next = NULL; + config->hash_func->prev = NULL; + } else { + if (!config->hash_func->next) { + config->hash_func->next = + silc_calloc(1, sizeof(*config->hash_func->next)); + config->hash_func->next->next = NULL; + config->hash_func->next->prev = config->hash_func; + config->hash_func = config->hash_func->next; + } + } + + /* Get Hash function name */ + ret = silc_config_get_token(line, &config->hash_func->alg_name); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Hash function name not defined\n", + config->filename, pc->linenum); + break; + } + + /* Get Hash function module name */ + config->hash_func->sim_name = NULL; + ret = silc_config_get_token(line, &config->hash_func->sim_name); + if (ret < 0) + break; + + /* Get block length */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Hash function block length not defined\n", + config->filename, pc->linenum); + break; + } + config->hash_func->block_len = atoi(tmp); + silc_free(tmp); + + /* Get hash length */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret == 0) { + fprintf(stderr, "%s:%d: Hash function hash length not defined\n", + config->filename, pc->linenum); + break; + } + config->hash_func->key_len = atoi(tmp); + silc_free(tmp); + + check = TRUE; + break; + + case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION: + + if (!config->conns) { + config->conns = silc_calloc(1, sizeof(*config->conns)); + config->conns->next = NULL; + config->conns->prev = NULL; + } else { + if (!config->conns->next) { + config->conns->next = silc_calloc(1, sizeof(*config->conns)); + config->conns->next->next = NULL; + config->conns->next->prev = config->conns; + config->conns = config->conns->next; + } + } + + /* Get host */ + ret = silc_config_get_token(line, &config->conns->host); + if (ret < 0) + break; + if (ret == 0) + /* Any host */ + config->conns->host = strdup("*"); + + /* Get authentication method */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret) { + if (strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD) && + strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) { + fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n", + config->filename, pc->linenum, tmp); + break; + } + + if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD)) + config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD; + + if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) + config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY; + + silc_free(tmp); + } + + /* Get authentication data */ + ret = silc_config_get_token(line, &config->conns->auth_data); + if (ret < 0) + break; + + /* Get port */ + ret = silc_config_get_token(line, &tmp); + if (ret < 0) + break; + if (ret) { + config->conns->port = atoi(tmp); + silc_free(tmp); + } + + check = TRUE; + break; + + case SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND: + + if (!config->commands) { + config->commands = silc_calloc(1, sizeof(*config->commands)); + config->commands->next = NULL; + config->commands->prev = NULL; + } else { + if (!config->commands->next) { + config->commands->next = silc_calloc(1, sizeof(*config->commands)); + config->commands->next->next = NULL; + config->commands->next->prev = config->commands; + config->commands = config->commands->next; + } + } + + /* Get command line (this may include parameters as well. They + will be parsed later with standard command parser when + executing particular command.) */ + config->commands->command = strdup(line->data); + if (ret < 0) + break; + + check = TRUE; + break; + + case SILC_CLIENT_CONFIG_SECTION_TYPE_NONE: + default: + break; + } + + /* Check for error */ + if (check == FALSE) { + /* Line could not be parsed */ + fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum); + break; + } + + pc = pc->next; + } + + if (check == FALSE) + return FALSE;; + + /* Before returning all the lists in the config object must be set + to their first values (the last value is first here). */ + while (config->cipher && config->cipher->prev) + config->cipher = config->cipher->prev; + while (config->pkcs && config->pkcs->prev) + config->pkcs = config->pkcs->prev; + while (config->hash_func && config->hash_func->prev) + config->hash_func = config->hash_func->prev; + while (config->conns && config->conns->prev) + config->conns = config->conns->prev; + while (config->commands && config->commands->prev) + config->commands = config->commands->prev; + + SILC_LOG_DEBUG(("Done")); + + return TRUE; +} + +/* Registers configured ciphers. These can then be allocated by the + client when needed. */ + +void silc_client_config_register_ciphers(SilcClientConfig config) +{ + SilcClientConfigSectionAlg *alg; + SilcClient client = (SilcClient)config->client; + + SILC_LOG_DEBUG(("Registering configured ciphers")); + + alg = config->cipher; + while(alg) { + + if (!alg->sim_name) { + /* Crypto module is supposed to be built in. Nothing to be done + here except to test that the cipher really is built in. */ + SilcCipher tmp = NULL; + + if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) { + SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name)); + silc_client_stop(client); + exit(1); + } + silc_cipher_free(tmp); + +#ifdef SILC_SIM + } else { + /* Load (try at least) the crypto SIM module */ + SilcCipherObject cipher; + SilcSimContext *sim; + + memset(&cipher, 0, sizeof(cipher)); + cipher.name = alg->alg_name; + cipher.block_len = alg->block_len; + cipher.key_len = alg->key_len * 8; + + sim = silc_sim_alloc(); + sim->type = SILC_SIM_CIPHER; + sim->libname = alg->sim_name; + + if ((silc_sim_load(sim))) { + cipher.set_key = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_CIPHER_SIM_SET_KEY)); + SILC_LOG_DEBUG(("set_key=%p", cipher.set_key)); + cipher.set_key_with_string = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_CIPHER_SIM_SET_KEY_WITH_STRING)); + SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string)); + cipher.encrypt = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_CIPHER_SIM_ENCRYPT_CBC)); + SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt)); + cipher.decrypt = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_CIPHER_SIM_DECRYPT_CBC)); + SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt)); + cipher.context_len = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_CIPHER_SIM_CONTEXT_LEN)); + SILC_LOG_DEBUG(("context_len=%p", cipher.context_len)); + + /* Put the SIM to the table of all SIM's in client */ + client->sim = silc_realloc(client->sim, + sizeof(*client->sim) * + (client->sim_count + 1)); + client->sim[client->sim_count] = sim; + client->sim_count++; + } else { + SILC_LOG_ERROR(("Error configuring ciphers")); + silc_client_stop(client); + exit(1); + } + + /* Register the cipher */ + silc_cipher_register(&cipher); +#endif + } + + alg = alg->next; + } +} + +/* Registers configured PKCS's. */ +/* XXX: This really doesn't do anything now since we have statically + registered our PKCS's. This should be implemented when PKCS works + as SIM's. This checks now only that the PKCS user requested is + really out there. */ + +void silc_client_config_register_pkcs(SilcClientConfig config) +{ + SilcClientConfigSectionAlg *alg = config->pkcs; + SilcClient client = (SilcClient)config->client; + SilcPKCS tmp = NULL; + + SILC_LOG_DEBUG(("Registering configured PKCS")); + + while(alg) { + + if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) { + SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name)); + silc_client_stop(client); + exit(1); + } + silc_free(tmp); + + alg = alg->next; + } +} + +/* Registers configured hash functions. These can then be allocated by the + client when needed. */ + +void silc_client_config_register_hashfuncs(SilcClientConfig config) +{ + SilcClientConfigSectionAlg *alg; + SilcClient client = (SilcClient)config->client; + + SILC_LOG_DEBUG(("Registering configured hash functions")); + + alg = config->hash_func; + while(alg) { + + if (!alg->sim_name) { + /* Hash module is supposed to be built in. Nothing to be done + here except to test that the hash function really is built in. */ + SilcHash tmp = NULL; + + if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) { + SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name)); + silc_client_stop(client); + exit(1); + } + silc_free(tmp); + +#ifdef SILC_SIM + } else { + /* Load (try at least) the hash SIM module */ + SilcHashObject hash; + SilcSimContext *sim; + + memset(&hash, 0, sizeof(hash)); + hash.name = alg->alg_name; + hash.block_len = alg->block_len; + hash.hash_len = alg->key_len; + + sim = silc_sim_alloc(); + sim->type = SILC_SIM_HASH; + sim->libname = alg->sim_name; + + if ((silc_sim_load(sim))) { + hash.init = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_HASH_SIM_INIT)); + SILC_LOG_DEBUG(("init=%p", hash.init)); + hash.update = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_HASH_SIM_UPDATE)); + SILC_LOG_DEBUG(("update=%p", hash.update)); + hash.final = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_HASH_SIM_FINAL)); + SILC_LOG_DEBUG(("final=%p", hash.final)); + hash.context_len = + silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, + SILC_HASH_SIM_CONTEXT_LEN)); + SILC_LOG_DEBUG(("context_len=%p", hash.context_len)); + + /* Put the SIM to the table of all SIM's in client */ + client->sim = silc_realloc(client->sim, + sizeof(*client->sim) * + (client->sim_count + 1)); + client->sim[client->sim_count] = sim; + client->sim_count++; + } else { + SILC_LOG_ERROR(("Error configuring hash functions")); + silc_client_stop(client); + exit(1); + } + + /* Register the cipher */ + silc_hash_register(&hash); +#endif + } + + alg = alg->next; + } +} + + +SilcClientConfigSectionConnection * +silc_client_config_find_connection(SilcClientConfig config, + char *host, int port) +{ + int i; + SilcClientConfigSectionConnection *conn = NULL; + + SILC_LOG_DEBUG(("Finding connection")); + + if (!host) + return NULL; + + if (!config->conns) + return NULL; + + conn = config->conns; + for (i = 0; conn; i++) { + if (silc_string_compare(conn->host, host)) + break; + conn = conn->next; + } + + if (!conn) + return NULL; + + SILC_LOG_DEBUG(("Found match")); + + return conn; +} diff --git a/apps/silc/clientconfig.h b/apps/silc/clientconfig.h new file mode 100644 index 00000000..f2c7a4af --- /dev/null +++ b/apps/silc/clientconfig.h @@ -0,0 +1,124 @@ +/* + + clientconfig.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef CLIENTCONFIG_H +#define CLIENTCONFIG_H + +/* Holds information of configured algorithms */ +typedef struct SilcClientConfigSectionAlgStruct { + char *alg_name; + char *sim_name; + unsigned int block_len; + unsigned int key_len; + struct SilcClientConfigSectionAlgStruct *next; + struct SilcClientConfigSectionAlgStruct *prev; +#define SILC_CLIENT_CONFIG_MODNAME "builtin" +} SilcClientConfigSectionAlg; + +/* Holds all server connections from config file */ +typedef struct SilcClientConfigSectionConnectionStruct { + char *host; + int auth_meth; + char *auth_data; + unsigned short port; + struct SilcClientConfigSectionConnectionStruct *next; + struct SilcClientConfigSectionConnectionStruct *prev; +#define SILC_CLIENT_CONFIG_AUTH_METH_PASSWD "passwd" +#define SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY "pubkey" +} SilcClientConfigSectionConnection; + +/* Holds all given commands from config file */ +typedef struct SilcClientConfigSectionCommandStruct { + char *command; + struct SilcClientConfigSectionCommandStruct *next; + struct SilcClientConfigSectionCommandStruct *prev; +} SilcClientConfigSectionCommand; + +/* + SILC Client Config object. + + This object holds all the data parsed from the SILC client configuration + file. This is mainly used at the initialization of the client. + +*/ +typedef struct { + /* Pointer back to the client */ + void *client; + + /* Filename of the configuration file */ + char *filename; + + /* Configuration sections */ + SilcClientConfigSectionAlg *cipher; + SilcClientConfigSectionAlg *pkcs; + SilcClientConfigSectionAlg *hash_func; + SilcClientConfigSectionConnection *conns; + SilcClientConfigSectionCommand *commands; +} SilcClientConfigObject; + +typedef SilcClientConfigObject *SilcClientConfig; + +/* Configuration section type enumerations. */ +typedef enum { + SILC_CLIENT_CONFIG_SECTION_TYPE_NONE = 0, + SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, + SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, + SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, + SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, + SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND = 253, /* Special section */ +} SilcClientConfigSectionType; + +/* SILC Configuration Section structure. */ +typedef struct { + const char *section; + SilcClientConfigSectionType type; + unsigned int maxfields; +} SilcClientConfigSection; + +/* List of all possible config sections in SILC client */ +extern SilcClientConfigSection silc_client_config_sections[]; + +/* Structure used in parsing the configuration lines. The line is read + from a file to this structure before parsing it further. */ +typedef struct SilcClientConfigParseStruct { + SilcBuffer line; + unsigned int linenum; + SilcClientConfigSection *section; + struct SilcClientConfigParseStruct *next; + struct SilcClientConfigParseStruct *prev; +} *SilcClientConfigParse; + +/* Prototypes */ +SilcClientConfig silc_client_config_alloc(char *filename); +void silc_client_config_free(SilcClientConfig config); +int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer, + SilcClientConfigParse *return_config); +int silc_client_config_parse_lines(SilcClientConfig config, + SilcClientConfigParse parse_config); +int silc_client_config_check_sections(unsigned int checkmask); +void silc_client_config_setlogfiles(SilcClientConfig config); +void silc_client_config_register_ciphers(SilcClientConfig config); +void silc_client_config_register_pkcs(SilcClientConfig config); +void silc_client_config_register_hashfuncs(SilcClientConfig config); +SilcClientConfigSectionConnection * +silc_client_config_find_connection(SilcClientConfig config, + char *host, int port); + +#endif diff --git a/apps/silc/clientutil.c b/apps/silc/clientutil.c new file mode 100644 index 00000000..8946b289 --- /dev/null +++ b/apps/silc/clientutil.c @@ -0,0 +1,444 @@ +/* + + client.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" + +/* Internal routine used to print lines to window. This can split the + line neatly if a word would overlap the line. */ + +static void silc_print_to_window(WINDOW *win, char *message) +{ + int str_len, len; + + str_len = strlen(message); + + if (str_len > COLS - 1) { + /* Split overlapping words to next line */ + /* XXX In principal this is wrong as this modifies the original + string as it replaces the last ' ' with '\n'. This could be done + with little more work so that it would not replace anything. */ + len = COLS - 1; + while (1) { + + while (len && message[len] != ' ') + len--; + + if (!len) + break; + + message[len] = '\n'; + len += COLS - 1; + if (len > str_len) + break; + } + } + + wprintw(win, "%s", message); + wrefresh(win); +} + +/* Prints a message with three star (*) sign before the actual message + on the current output window. This is used to print command outputs + and error messages. */ +/* XXX Change to accept SilcClientWindow and use output window + from there (the pointer to the output window must be added to the + SilcClientWindow object. */ + +void silc_say(SilcClient client, char *msg, ...) +{ + va_list vp; + char message[1024]; + + memset(message, 0, sizeof(message)); + strncat(message, "\n*** ", 5); + + va_start(vp, msg); + vsprintf(message + 5, msg, vp); + va_end(vp); + + /* Print the message */ + silc_print_to_window(client->screen->output_win[0], message); +} + +/* Prints message to the screen. This is used to print the messages + user is typed and message that came on channels. */ + +void silc_print(SilcClient client, char *msg, ...) +{ + va_list vp; + char message[1024]; + + memset(message, 0, sizeof(message)); + strncat(message, "\n ", 2); + + va_start(vp, msg); + vsprintf(message + 1, msg, vp); + va_end(vp); + + /* Print the message */ + silc_print_to_window(client->screen->output_win[0], message); +} + +/* Returns user's mail path */ + +char *silc_get_mail_path() +{ + char pathbuf[MAXPATHLEN]; + char *path; + + if ((path = (char *)getenv("MAIL")) != 0) { + strncpy(pathbuf, path, strlen(path)); + } else { + strcpy(pathbuf, _PATH_MAILDIR); + strcat(pathbuf, "/"); + strcat(pathbuf, silc_get_username()); + } + + return strdup(pathbuf); +} + +/* gets the number of the user's mails, if possible */ + +int silc_get_number_of_emails() +{ + FILE *tl; + int num = 0; + char *filename; + char data[1024]; + + filename = silc_get_mail_path(); + + tl = fopen(filename, "r"); + if (!tl) { + fprintf(stderr, "Couldn't open mail file (%s).\n", filename); + } else { + while((fscanf(tl, "%s", data)) != EOF) { + if(!strcmp(data, "Subject:")) + num++; + } + + fclose(tl); + } + + return num; +} + +/* Returns the username of the user. If the global variable LOGNAME + does not exists we will get the name from the password file. */ + +char *silc_get_username() +{ + char *logname = NULL; + + logname = strdup(getenv("LOGNAME")); + if (!logname) { + logname = getlogin(); + if (!logname) { + struct passwd *pw; + + pw = getpwuid(getuid()); + if (!pw) { + fprintf(stderr, "silc_get_username: %s\n", strerror(errno)); + return NULL; + } + + logname = strdup(pw->pw_name); + } + } + + return logname; +} + +/* Returns the real name of ther user. */ + +char *silc_get_real_name() +{ + char *realname = NULL; + struct passwd *pw; + + pw = getpwuid(getuid()); + if (!pw) { + fprintf(stderr, "silc_get_username: %s\n", strerror(errno)); + return NULL; + } + + if (strchr(pw->pw_gecos, ',')) + *strchr(pw->pw_gecos, ',') = 0; + + realname = strdup(pw->pw_gecos); + + return realname; +} + +/* Returns time til next minute changes. Used to update the clock when + needed. */ + +int silc_client_time_til_next_min() +{ + time_t curtime; + struct tm *min; + + curtime = time(0); + min = localtime(&curtime); + + return 60 - min->tm_sec; +} + +/* Asks passphrase from user on the input line. */ + +char *silc_client_ask_passphrase(SilcClient client) +{ + char pass1[256], pass2[256]; + char *ret; + int try = 3; + + while(try) { + + /* Print prompt */ + wattroff(client->screen->input_win, A_INVIS); + silc_screen_input_print_prompt(client->screen, "Passphrase: "); + wattron(client->screen->input_win, A_INVIS); + + /* Get string */ + memset(pass1, 0, sizeof(pass1)); + wgetnstr(client->screen->input_win, pass1, sizeof(pass1)); + + /* Print retype prompt */ + wattroff(client->screen->input_win, A_INVIS); + silc_screen_input_print_prompt(client->screen, "Retype passphrase: "); + wattron(client->screen->input_win, A_INVIS); + + /* Get string */ + memset(pass2, 0, sizeof(pass2)); + wgetnstr(client->screen->input_win, pass2, sizeof(pass2)); + + if (!strncmp(pass1, pass2, strlen(pass2))) + break; + + try--; + } + + ret = silc_calloc(strlen(pass1), sizeof(char)); + memcpy(ret, pass1, strlen(pass1)); + + memset(pass1, 0, sizeof(pass1)); + memset(pass2, 0, sizeof(pass2)); + + wattroff(client->screen->input_win, A_INVIS); + silc_screen_input_reset(client->screen); + + return ret; +} + +/* Lists supported (builtin) ciphers */ + +void silc_client_list_ciphers() +{ + +} + +/* Lists supported (builtin) hash functions */ + +void silc_client_list_hash_funcs() +{ + +} + +/* Lists supported PKCS algorithms */ + +void silc_client_list_pkcs() +{ + +} + +/* Displays input prompt on command line and takes input data from user */ + +char *silc_client_get_input(const char *prompt) +{ + char input[2048]; + int fd; + + fd = open("/dev/tty", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "silc: %s\n", strerror(errno)); + exit(1); + } + + memset(input, 0, sizeof(input)); + + printf("%s", prompt); + fflush(stdout); + + if ((read(fd, input, sizeof(input))) < 0) { + fprintf(stderr, "silc: %s\n", strerror(errno)); + exit(1); + } + + if (strlen(input) <= 1) + return NULL; + + if (strchr(input, '\n')) + *strchr(input, '\n') = '\0'; + + return strdup(input); +} + +/* Displays prompt on command line and takes passphrase with echo + off from user. */ + +char *silc_client_get_passphrase(const char *prompt) +{ +#if 0 + char input[2048]; + char *ret; + int fd; + struct termios to; + struct termios to_old; + + fd = open("/dev/tty", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "silc: %s\n", strerror(errno)); + exit(1); + } + + signal(SIGINT, SIG_IGN); + + /* Get terminal info */ + tcgetattr(fd, &to); + to_old = to; + + /* Echo OFF */ + to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + tcsetattr(fd, TCSANOW, &to); + + memset(input, 0, sizeof(input)); + + printf("%s", prompt); + fflush(stdout); + + if ((read(fd, input, sizeof(input))) < 0) { + fprintf(stderr, "silc: %s\n", strerror(errno)); + exit(1); + } + + if (strlen(input) <= 1) { + tcsetattr(fd, TCSANOW, &to_old); + return NULL; + } + + if (strchr(input, '\n')) + *strchr(input, '\n') = '\0'; + + /* Restore old terminfo */ + tcsetattr(fd, TCSANOW, &to_old); + signal(SIGINT, SIG_DFL); + + ret = silc_calloc(strlen(input), sizeof(char)); + memcpy(ret, input, strlen(input)); + memset(input, 0, sizeof(input)); + return ret; +#else + return NULL; +#endif +} + +/* Creates new public key and private key pair. This is used only + when user wants to create new key pair from command line. */ + +void silc_client_create_key_pair(char *pkcs_name, int bits) +{ + SilcPKCS pkcs; + SilcRng rng; + unsigned char *key; + unsigned int key_len; + char *pkfile = NULL, *prvfile = NULL; + + printf("\ +New pair of keys will be created. Please, answer to following questions.\n\ +"); + + if (!pkcs_name) { + again_name: + pkcs_name = + silc_client_get_input("PKCS name (l to list names) [rsa]: "); + if (!pkcs_name) + pkcs_name = strdup("rsa"); + + if (*pkcs_name == 'l' || *pkcs_name == 'L') { + silc_client_list_pkcs(); + silc_free(pkcs_name); + goto again_name; + } + } + + if (!bits) { + char *length = NULL; + length = + silc_client_get_input("Key length in bits [1024]: "); + if (!length) + bits = 1024; + else + bits = atoi(length); + } + + rng = silc_rng_alloc(); + silc_rng_init(rng); + silc_math_primegen_init(); + + again_pk: + pkfile = silc_client_get_input("Public key filename: "); + if (!pkfile) { + printf("Public key filename must be defined\n"); + goto again_pk; + } + + again_prv: + prvfile = silc_client_get_input("Private key filename: "); + if (!prvfile) { + printf("Private key filename must be defined\n"); + goto again_prv; + } + + /* Generate keys */ + silc_pkcs_alloc(pkcs_name, &pkcs); + pkcs->pkcs->init(pkcs->context, bits, rng); + + /* Save keys into file */ + key = silc_pkcs_get_public_key(pkcs, &key_len); + silc_pkcs_save_public_key(pkcs, pkfile, key, key_len); + memset(key, 0, sizeof(key_len)); + silc_free(key); + key = silc_pkcs_get_private_key(pkcs, &key_len); + silc_pkcs_save_private_key(pkcs, prvfile, key, key_len, ""); + memset(key, 0, sizeof(key_len)); + silc_free(key); + + silc_math_primegen_uninit(); + silc_rng_free(rng); + silc_pkcs_free(pkcs); +} diff --git a/apps/silc/clientutil.h b/apps/silc/clientutil.h new file mode 100644 index 00000000..e18cf2d7 --- /dev/null +++ b/apps/silc/clientutil.h @@ -0,0 +1,40 @@ +/* + + client.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef CLIENTUTIL_H +#define CLIENTUTIL_H + +/* Prototypes */ +void silc_say(SilcClient client, char *msg, ...); +void silc_print(SilcClient client, char *msg, ...); +char *silc_get_mail_path(); +int silc_get_number_of_emails(); +char *silc_get_username(); +char *silc_get_real_name(); +int silc_client_time_til_next_min(); +char *silc_client_ask_passphrase(SilcClient client); +char *silc_client_get_input(const char *prompt); +char *silc_client_get_passphrase(const char *prompt); +void silc_client_list_ciphers(); +void silc_client_list_hash_funcs(); +void silc_client_list_pkcs(); +void silc_client_create_key_pair(char *pkcs_name, int bits); + +#endif diff --git a/apps/silc/command.c b/apps/silc/command.c new file mode 100644 index 00000000..5c64ca8f --- /dev/null +++ b/apps/silc/command.c @@ -0,0 +1,584 @@ +/* + + command.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" + +/* Client command list. */ +SilcClientCommand silc_command_list[] = +{ + SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3), + SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3), + SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", + SILC_CF_LAG | SILC_CF_REG, 3), + SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1), + SILC_CLIENT_CMD(kill, KILL, "KILL", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2), + SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(connect, CONNECT, "CONNECT", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2), + SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(oper, OPER, "OPER", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2), + SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(restart, RESTART, "RESTART", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2), + SILC_CLIENT_CMD(close, CLOSE, "CLOSE", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2), + SILC_CLIENT_CMD(die, DIE, "DIE", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2), + SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER", + SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2), + SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2), + SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2), + + /* + * Local. client specific commands + */ + SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2), + SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1), + SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1), + SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2), + SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3), + SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2), + + { NULL, 0, NULL, 0}, +}; + +/* List of pending commands. */ +SilcClientCommandPending *silc_command_pending = NULL; + +/* Add new pending command to the list of pending commands. Currently + pending commands are executed from command replies, thus we can + execute any command after receiving some specific command reply. + + The argument `reply_cmd' is the command reply from where the callback + function is to be called, thus, it IS NOT the command to be executed. + + XXX: If needed in the future this support may be extended for + commands as well, when any command could be executed after executing + some specific command. */ + +void silc_client_command_pending(SilcCommand reply_cmd, + SilcClientCommandCallback callback, + void *context) +{ + SilcClientCommandPending *reply, *r; + + reply = silc_calloc(1, sizeof(*reply)); + reply->reply_cmd = reply_cmd; + reply->context = context; + reply->callback = callback; + + if (silc_command_pending == NULL) { + silc_command_pending = reply; + return; + } + + for (r = silc_command_pending; r; r = r->next) { + if (r->next == NULL) { + r->next = reply; + break; + } + } +} + +/* Deletes pending command by reply command type. */ + +void silc_client_command_pending_del(SilcCommand reply_cmd) +{ + SilcClientCommandPending *r, *tmp; + + if (silc_command_pending) { + if (silc_command_pending->reply_cmd == reply_cmd) { + silc_free(silc_command_pending); + silc_command_pending = NULL; + return; + } + + for (r = silc_command_pending; r; r = r->next) { + if (r->next && r->next->reply_cmd == reply_cmd) { + tmp = r->next; + r->next = r->next->next; + silc_free(tmp); + break; + } + } + } +} + +/* Free command context and its internals */ + +static void silc_client_command_free(SilcClientCommandContext cmd) +{ + int i; + + if (cmd) { + for (i = 0; i < cmd->argc; i++) + silc_free(cmd->argv[i]); + silc_free(cmd); + } +} + +/* Command WHOIS. This command is used to query information about + specific user. */ + +SILC_CLIENT_CMD_FUNC(whois) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcBuffer buffer; + + if (cmd->argc < 2 || cmd->argc > 3) { + silc_say(cmd->client, "Usage: /WHOIS [@] []"); + goto out; + } + + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + } + + buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS, + cmd->argc - 1, ++cmd->argv, + ++cmd->argv_lens, ++cmd->argv_types); + silc_client_packet_send(cmd->client, cmd->client->current_win->sock, + SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, + buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + cmd->argv--; + cmd->argv_lens--; + cmd->argv_types--; + + out: + silc_client_command_free(cmd); +} + +SILC_CLIENT_CMD_FUNC(whowas) +{ +} + +/* Command IDENTIFY. This command is used to query information about + specific user, especially ID's. */ + +SILC_CLIENT_CMD_FUNC(identify) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcBuffer buffer; + + if (cmd->argc < 2 || cmd->argc > 3) { + silc_say(cmd->client, "Usage: /IDENTIFY [@] []"); + goto out; + } + + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + } + + buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY, + cmd->argc - 1, ++cmd->argv, + ++cmd->argv_lens, ++cmd->argv_types); + silc_client_packet_send(cmd->client, cmd->client->current_win->sock, + SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, + buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + cmd->argv--; + cmd->argv_lens--; + cmd->argv_types--; + + out: + silc_client_command_free(cmd); +} + +/* Command NICK. Shows current nickname/sets new nickname on current + window. */ + +SILC_CLIENT_CMD_FUNC(nick) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientWindow win = NULL; + SilcBuffer buffer; + + if (!cmd->sock) { + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + } + + /* Show current nickname */ + if (cmd->argc < 2) { + if (cmd->sock) { + silc_say(cmd->client, "Your nickname is %s on server %s", + win->nickname, win->remote_host); + } else { + silc_say(cmd->client, "Your nickname is %s", win->nickname); + } + goto out; + } + + win = (SilcClientWindow)cmd->sock->user_data; + + /* Set new nickname */ + buffer = silc_command_encode_payload(SILC_COMMAND_NICK, + cmd->argc - 1, ++cmd->argv, + ++cmd->argv_lens, ++cmd->argv_types); + silc_client_packet_send(cmd->client, cmd->sock, + SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, + buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + cmd->argv--; + cmd->argv_lens--; + cmd->argv_types--; + if (win->nickname) + silc_free(win->nickname); + win->nickname = strdup(cmd->argv[1]); + + out: + silc_client_command_free(cmd); +} + +/* Command SERVER. Connects to remote SILC server. This is local command. */ + +SILC_CLIENT_CMD_FUNC(server) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + int len, port; + char *hostname; + + if (cmd->argc < 2) { + /* Show current servers */ + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, "You are not connected to any server"); + silc_say(cmd->client, "Usage: /SERVER [[:]]"); + goto out; + } + + goto out; + } + + /* See if port is included and then extract it */ + if (strchr(cmd->argv[1], ':')) { + len = strcspn(cmd->argv[1], ":"); + hostname = silc_calloc(len + 1, sizeof(char)); + memcpy(hostname, cmd->argv[1], len); + port = atoi(cmd->argv[1] + 1 + len); + } else { + hostname = cmd->argv[1]; + /* XXX */ + port = 334; + } + + /* Connect asynchronously to not to block user interface */ + silc_client_connect_to_server(cmd->client, port, hostname); + + out: + silc_client_command_free(cmd); +} + +SILC_CLIENT_CMD_FUNC(list) +{ +} + +SILC_CLIENT_CMD_FUNC(topic) +{ +} + +SILC_CLIENT_CMD_FUNC(invite) +{ +} + +/* Command QUIT. Closes connection with current server. */ + +SILC_CLIENT_CMD_FUNC(quit) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcBuffer buffer; + + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + } + + buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, + ++cmd->argv, ++cmd->argv_lens, + ++cmd->argv_types); + silc_client_packet_send(cmd->client, cmd->client->current_win->sock, + SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, + buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + cmd->argv--; + cmd->argv_lens--; + cmd->argv_types--; + + /* Close connection */ + silc_client_close_connection(cmd->client, cmd->sock); + cmd->client->screen->bottom_line->connection = NULL; + silc_screen_print_bottom_line(cmd->client->screen, 0); + + silc_client_command_free(cmd); +} + +SILC_CLIENT_CMD_FUNC(kill) +{ +} + +SILC_CLIENT_CMD_FUNC(info) +{ +} + +SILC_CLIENT_CMD_FUNC(connect) +{ +} + +SILC_CLIENT_CMD_FUNC(ping) +{ +} + +SILC_CLIENT_CMD_FUNC(oper) +{ +} + +SILC_CLIENT_CMD_FUNC(trace) +{ +} + +SILC_CLIENT_CMD_FUNC(notice) +{ +} + +/* Command JOIN. Joins to a channel. */ + +SILC_CLIENT_CMD_FUNC(join) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientWindow win = NULL; + SilcIDCache *id_cache = NULL; + SilcBuffer buffer; + +#define CIDC(x) win->channel_id_cache[(x) - 32] +#define CIDCC(x) win->channel_id_cache_count[(x) - 32] + + if (cmd->argc < 2) { + /* Show channels currently joined to */ + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, "No current channel for this window"); + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + + } + + goto out; + } + + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + } + + win = (SilcClientWindow)cmd->sock->user_data; + + /* See if we have joined to the requested channel already */ + silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]), + cmd->argv[1], &id_cache); + + if (id_cache) { + silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]); + win->current_channel = (SilcChannelEntry)id_cache->context; + cmd->client->screen->bottom_line->channel = cmd->argv[1]; + silc_screen_print_bottom_line(cmd->client->screen, 0); + goto out; + } + + /* Send JOIN command to the server */ + buffer = silc_command_encode_payload(SILC_COMMAND_JOIN, + cmd->argc - 1, ++cmd->argv, + ++cmd->argv_lens, ++cmd->argv_types); + silc_client_packet_send(cmd->client, cmd->client->current_win->sock, + SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, + buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + cmd->argv--; + cmd->argv_lens--; + cmd->argv_types--; + + out: + silc_client_command_free(cmd); +#undef CIDC +#undef CIDCC +} + +SILC_CLIENT_CMD_FUNC(motd) +{ +} + +SILC_CLIENT_CMD_FUNC(umode) +{ +} + +SILC_CLIENT_CMD_FUNC(cmode) +{ +} + +SILC_CLIENT_CMD_FUNC(kick) +{ +} + +SILC_CLIENT_CMD_FUNC(restart) +{ +} + +SILC_CLIENT_CMD_FUNC(close) +{ +} + +SILC_CLIENT_CMD_FUNC(die) +{ +} + +SILC_CLIENT_CMD_FUNC(silcoper) +{ +} + +SILC_CLIENT_CMD_FUNC(leave) +{ +} + +SILC_CLIENT_CMD_FUNC(names) +{ +} + +/* + * Local commands + */ + +/* HELP command. This is local command and shows help on SILC */ + +SILC_CLIENT_CMD_FUNC(help) +{ + +} + +/* CLEAR command. This is local command and clears current output window */ + +SILC_CLIENT_CMD_FUNC(clear) +{ + SilcClient client = (SilcClient)context; + + assert(client->current_win != NULL); + wclear((WINDOW *)client->current_win->screen); + wrefresh((WINDOW *)client->current_win->screen); +} + +/* VERSION command. This is local command and shows version of the client */ + +SILC_CLIENT_CMD_FUNC(version) +{ + +} + +/* Command MSG. Sends private message to user or list of users. */ +/* XXX supports only one destination */ + +SILC_CLIENT_CMD_FUNC(msg) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientWindow win = NULL; + SilcClient client = cmd->client; + SilcBuffer buffer; + SilcIDCache *id_cache; + unsigned int nick_len; + + if (cmd->argc < 3) { + silc_say(cmd->client, "Usage: /MSG "); + goto out; + } + + if (!cmd->client->current_win->sock) { + silc_say(cmd->client, + "You are not connected to a server, use /SERVER to connect"); + goto out; + } + + win = (SilcClientWindow)cmd->sock->user_data; + +#define CIDC(x) win->client_id_cache[(x) - 32], \ + win->client_id_cache_count[(x) - 32] + + /* Find ID from cache */ + if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1], + &id_cache) == FALSE) { + SilcClientCommandContext ctx; + char ident[512]; + + SILC_LOG_DEBUG(("Requesting Client ID from server")); + + /* No ID found. Do query from the server. The query is done by + sending simple IDENTIFY command to the server. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->sock = cmd->sock; + memset(ident, 0, sizeof(ident)); + snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]); + silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, + &ctx->argv_types, &ctx->argc, 2); + silc_client_command_identify(ctx); + + /* Mark this command to be pending command and to be executed after + we have received the IDENTIFY reply from server. */ + silc_client_command_pending(SILC_COMMAND_IDENTIFY, + silc_client_command_msg, context); + return; + } + + /* Display the message for our eyes. */ + silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]); + + /* Send the private message */ + silc_client_packet_send_private_message(client, cmd->sock, id_cache->context, + cmd->argv[2], cmd->argv_lens[2], + TRUE); + out: + silc_client_command_free(cmd); +#undef CIDC +} + +SILC_CLIENT_CMD_FUNC(away) +{ +} diff --git a/apps/silc/command.h b/apps/silc/command.h new file mode 100644 index 00000000..19db809b --- /dev/null +++ b/apps/silc/command.h @@ -0,0 +1,164 @@ +/* + + command.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef COMMAND_H +#define COMMAND_H + +/* + Structure holding one command and pointer to its function. + + SilcCommandCb cb + + Callback function called when this command is executed. + + SilcCommand cmd + + The actual command. These are defined in silccore/silccommand.h + + char *name + + Logical name of the command. This is the visible command name + that user uses when calling command. Eg. NICK. + + SilcCommandFlag flags + + Flags for the command. These set how command behaves on different + situations. Server sets these flags as well, but to be sure + that our client never sends wrong commands we preserve the + flags on client side as well. + + XXX: We preserve these so that we define them but currently we + don't check the flags at all. + +*/ +typedef struct { + SilcCommandCb cb; + SilcCommand cmd; + char *name; + SilcCommandFlag flags; + unsigned int max_args; +} SilcClientCommand; + +/* All client commands */ +extern SilcClientCommand silc_command_list[]; + +/* Client command callback function. This included into Command Context, + and if it is defined it will be executed when executing the command. */ +typedef void (*SilcClientCommandCallback)(void *context); + +/* Context sent as argument to all commands */ +typedef struct { + SilcClient client; + SilcSocketConnection sock; + unsigned int argc; + unsigned char **argv; + unsigned int *argv_lens; + unsigned int *argv_types; +} *SilcClientCommandContext; + +/* Structure holding pending commands. If command is pending it will be + executed after command reply has been executed. */ +/* XXX This support may added for commands as well and not just command + replies, if needed later. */ +typedef struct SilcClientCommandPendingStruct { + SilcCommand reply_cmd; + void *context; + SilcClientCommandCallback callback; + + struct SilcClientCommandPendingStruct *next; +} SilcClientCommandPending; + +/* List of pending commands */ +extern SilcClientCommandPending *silc_command_pending; + +/* Macros */ + +/* Macro used for command declaration in command list structure */ +#define SILC_CLIENT_CMD(func, cmd, name, flags, args) \ +{ silc_client_command_##func, SILC_COMMAND_##cmd, name, flags, args } + +/* Macro used to declare command functions */ +#define SILC_CLIENT_CMD_FUNC(func) \ +void silc_client_command_##func(void *context) + +/* Checks for pending commands */ +#define SILC_CLIENT_COMMAND_CHECK_PENDING(ctx) \ +do { \ + if (silc_command_pending) { \ + SilcClientCommandPending *r; \ + SilcCommand cmd; \ + \ + cmd = silc_command_get(payload); \ + for (r = silc_command_pending; r; r = r->next) { \ + if (r->reply_cmd == cmd) { \ + ctx->context = r->context; \ + ctx->callback = r->callback; \ + break; \ + } \ + } \ + } \ +} while(0) + +/* Executed pending command */ +#define SILC_CLIENT_COMMAND_EXEC_PENDING(ctx, cmd) \ +do { \ + if (ctx->callback) { \ + (*ctx->callback)(ctx->context); \ + silc_client_command_pending_del((cmd)); \ + } \ +} while(0) + +/* Prototypes */ +void silc_client_command_pending(SilcCommand reply_cmd, + SilcClientCommandCallback callback, + void *context); +void silc_client_command_pending_del(SilcCommand reply_cmd); +SILC_CLIENT_CMD_FUNC(whois); +SILC_CLIENT_CMD_FUNC(whowas); +SILC_CLIENT_CMD_FUNC(identify); +SILC_CLIENT_CMD_FUNC(nick); +SILC_CLIENT_CMD_FUNC(server); +SILC_CLIENT_CMD_FUNC(list); +SILC_CLIENT_CMD_FUNC(topic); +SILC_CLIENT_CMD_FUNC(invite); +SILC_CLIENT_CMD_FUNC(quit); +SILC_CLIENT_CMD_FUNC(kill); +SILC_CLIENT_CMD_FUNC(info); +SILC_CLIENT_CMD_FUNC(connect); +SILC_CLIENT_CMD_FUNC(ping); +SILC_CLIENT_CMD_FUNC(oper); +SILC_CLIENT_CMD_FUNC(join); +SILC_CLIENT_CMD_FUNC(motd); +SILC_CLIENT_CMD_FUNC(umode); +SILC_CLIENT_CMD_FUNC(cmode); +SILC_CLIENT_CMD_FUNC(kick); +SILC_CLIENT_CMD_FUNC(restart); +SILC_CLIENT_CMD_FUNC(close); +SILC_CLIENT_CMD_FUNC(die); +SILC_CLIENT_CMD_FUNC(silcoper); +SILC_CLIENT_CMD_FUNC(leave); +SILC_CLIENT_CMD_FUNC(names); +SILC_CLIENT_CMD_FUNC(help); +SILC_CLIENT_CMD_FUNC(clear); +SILC_CLIENT_CMD_FUNC(version); +SILC_CLIENT_CMD_FUNC(msg); +SILC_CLIENT_CMD_FUNC(away); + +#endif diff --git a/apps/silc/command_reply.c b/apps/silc/command_reply.c new file mode 100644 index 00000000..3b54072a --- /dev/null +++ b/apps/silc/command_reply.c @@ -0,0 +1,551 @@ +/* + + command_reply.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * Command reply functions are "the otherside" of the command functions. + * Reply to a command sent by server is handled by these functions. + */ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" + +/* Client command reply list. */ +SilcClientCommandReply silc_command_reply_list[] = +{ + SILC_CLIENT_CMD_REPLY(whois, WHOIS), + SILC_CLIENT_CMD_REPLY(whowas, WHOWAS), + SILC_CLIENT_CMD_REPLY(identify, IDENTIFY), + SILC_CLIENT_CMD_REPLY(nick, NICK), + SILC_CLIENT_CMD_REPLY(list, LIST), + SILC_CLIENT_CMD_REPLY(topic, TOPIC), + SILC_CLIENT_CMD_REPLY(invite, INVITE), + SILC_CLIENT_CMD_REPLY(quit, QUIT), + SILC_CLIENT_CMD_REPLY(kill, KILL), + SILC_CLIENT_CMD_REPLY(info, INFO), + SILC_CLIENT_CMD_REPLY(away, AWAY), + SILC_CLIENT_CMD_REPLY(connect, CONNECT), + SILC_CLIENT_CMD_REPLY(ping, PING), + SILC_CLIENT_CMD_REPLY(oper, OPER), + SILC_CLIENT_CMD_REPLY(join, JOIN), + SILC_CLIENT_CMD_REPLY(motd, MOTD), + SILC_CLIENT_CMD_REPLY(umode, UMODE), + SILC_CLIENT_CMD_REPLY(cmode, CMODE), + SILC_CLIENT_CMD_REPLY(kick, KICK), + SILC_CLIENT_CMD_REPLY(restart, RESTART), + SILC_CLIENT_CMD_REPLY(close, CLOSE), + SILC_CLIENT_CMD_REPLY(die, DIE), + SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER), + SILC_CLIENT_CMD_REPLY(leave, LEAVE), + SILC_CLIENT_CMD_REPLY(names, LEAVE), + + { NULL, 0 }, +}; + +/* Status message structure. Messages are defined below. */ +typedef struct { + SilcCommandStatus status; + char *message; +} SilcCommandStatusMessage; + +/* Status messages returned by the server */ +#define STAT(x) SILC_STATUS_ERR_##x +const SilcCommandStatusMessage silc_command_status_messages[] = { + + { STAT(NO_SUCH_NICK), "No such nickname" }, + { STAT(NO_SUCH_CHANNEL), "No such channel" }, + { STAT(NO_SUCH_SERVER), "No such server" }, + { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" }, + { STAT(NO_RECIPIENT), "No recipient given" }, + { STAT(UNKNOWN_COMMAND), "Unknown command" }, + { STAT(WILDCARDS), "Unknown command" }, + { STAT(NO_CLIENT_ID), "No Client ID given" }, + { STAT(NO_CHANNEL_ID), "No Channel ID given" }, + { STAT(BAD_CLIENT_ID), "Bad Client ID" }, + { STAT(BAD_CHANNEL_ID), "Bad Channel ID" }, + { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" }, + { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" }, + { STAT(NICKNAME_IN_USE), "Nickname already exists" }, + { STAT(NOT_ON_CHANNEL), "You are not on that channel" }, + { STAT(USER_ON_CHANNEL), "User already on channel" }, + { STAT(NOT_REGISTERED), "You have not registered" }, + { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" }, + { STAT(TOO_MANY_PARAMS), "Too many parameters" }, + { STAT(PERM_DENIED), "Your host is not among the privileged" }, + { STAT(BANNED_FROM_SERVER),"You are banned from this server" }, + { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" }, + { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" }, + { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" }, + { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" }, + { STAT(UNKNOWN_MODE), "Unknown mode" }, + { STAT(NOT_YOU), "Cannot change mode for other users" }, + { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" }, + { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" }, + { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" }, + { STAT(BAD_NICKNAME), "Bad nickname" }, + { STAT(BAD_CHANNEL), "Bad channel name" }, + { STAT(AUTH_FAILED), "Authentication failed" }, + + { 0, NULL } +}; + +/* Process received command reply. */ + +void silc_client_command_reply_process(SilcClient client, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcClientCommandReplyContext ctx; + SilcCommandPayload payload; + + /* Get command reply payload from packet */ + payload = silc_command_parse_payload(buffer); + if (!payload) { + /* Silently ignore bad reply packet */ + SILC_LOG_DEBUG(("Bad command reply packet")); + return; + } + + /* Allocate command reply context. This must be free'd by the + command reply routine receiving it. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->client = client; + ctx->sock = sock; + ctx->payload = payload; + + /* Check for pending commands and mark to be exeucted */ + SILC_CLIENT_COMMAND_CHECK_PENDING(ctx); + + /* Execute command reply */ + SILC_CLIENT_COMMAND_REPLY_EXEC(ctx); +} + +/* Returns status message string */ + +static char * +silc_client_command_status_message(SilcCommandStatus status) +{ + int i; + + for (i = 0; silc_command_status_messages[i].message; i++) { + if (silc_command_status_messages[i].status == status) + break; + } + + if (silc_command_status_messages[i].message == NULL) + return NULL; + + return silc_command_status_messages[i].message; +} + +/* Free command reply context and its internals. */ + +void silc_client_command_reply_free(SilcClientCommandReplyContext cmd) +{ + if (cmd) { + silc_command_free_payload(cmd->payload); + silc_free(cmd); + } +} + +/* Received reply for WHOIS command. This maybe called several times + for one WHOIS command as server may reply with list of results. */ + +SILC_CLIENT_CMD_REPLY_FUNC(whois) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClient client = cmd->client; + SilcCommandStatus status; + unsigned char *tmp; + + SILC_LOG_DEBUG(("Start")); + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { + tmp += 2; + silc_say(cmd->client, "%s: %s", tmp, + silc_client_command_status_message(status)); + goto out; + } else { + silc_say(cmd->client, "%s", silc_client_command_status_message(status)); + goto out; + } + } + + /* Display one whois reply */ + if (status == SILC_STATUS_OK) { + char buf[256]; + int argc, len; + unsigned char *id_data; + char *nickname = NULL, *username = NULL; + char *realname = NULL; + void *id; + + memset(buf, 0, sizeof(buf)); + + argc = silc_command_get_arg_num(cmd->payload); + id_data = silc_command_get_arg_type(cmd->payload, 2, NULL); + + nickname = silc_command_get_arg_type(cmd->payload, 3, &len); + if (nickname) { + strncat(buf, nickname, len); + strncat(buf, " is ", 4); + } + + username = silc_command_get_arg_type(cmd->payload, 4, &len); + if (username) { + strncat(buf, username, len); + } + + realname = silc_command_get_arg_type(cmd->payload, 5, &len); + if (realname) { + strncat(buf, " (", 2); + strncat(buf, realname, len); + strncat(buf, ")", 1); + } + +#if 0 + /* Save received Client ID to ID cache */ + /* XXX Maybe should not be saved as /MSG will get confused */ + id = silc_id_str2id(id_data, SILC_ID_CLIENT); + client->current_win->client_id_cache_count[(int)nickname[0] - 32] = + silc_idcache_add(&client->current_win-> + client_id_cache[(int)nickname[0] - 32], + client->current_win-> + client_id_cache_count[(int)nickname[0] - 32], + strdup(nickname), SILC_ID_CLIENT, id, NULL); +#endif + + silc_say(cmd->client, "%s", buf); + } + + if (status == SILC_STATUS_LIST_START) { + + } + + if (status == SILC_STATUS_LIST_END) { + + } + + SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS); + + out: + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(whowas) +{ +} + +/* Received reply for IDENTIFY command. This maybe called several times + for one IDENTIFY command as server may reply with list of results. + This is totally silent and does not print anything on screen. */ + +SILC_CLIENT_CMD_REPLY_FUNC(identify) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; + SilcClientEntry client_entry; + SilcCommandStatus status; + unsigned char *tmp; + + SILC_LOG_DEBUG(("Start")); + +#define CIDC(x) win->client_id_cache[(x) - 32] +#define CIDCC(x) win->client_id_cache_count[(x) - 32] + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { + tmp += 2; + silc_say(cmd->client, "%s: %s", tmp, + silc_client_command_status_message(status)); + goto out; + } else { + silc_say(cmd->client, "%s", silc_client_command_status_message(status)); + goto out; + } + } + + /* Display one whois reply */ + if (status == SILC_STATUS_OK) { + unsigned char *id_data; + char *nickname; + + id_data = silc_command_get_arg_type(cmd->payload, 2, NULL); + nickname = silc_command_get_arg_type(cmd->payload, 3, NULL); + + /* Allocate client entry */ + client_entry = silc_calloc(1, sizeof(*client_entry)); + client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT); + client_entry->nickname = strdup(nickname); + + /* Save received Client ID to ID cache */ + CIDCC(nickname[0]) = + silc_idcache_add(&CIDC(nickname[0]), CIDCC(nickname[0]), + client_entry->nickname, SILC_ID_CLIENT, + client_entry->id, client_entry); + } + + if (status == SILC_STATUS_LIST_START) { + + } + + if (status == SILC_STATUS_LIST_END) { + + } + + SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY); + + out: + silc_client_command_reply_free(cmd); +#undef CIDC +#undef CIDCC +} + +/* Received reply for command NICK. If everything went without errors + we just received our new Client ID. */ + +SILC_CLIENT_CMD_REPLY_FUNC(nick) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; + SilcCommandStatus status; + unsigned char *tmp, *id_string; + int argc; + + SILC_LOG_DEBUG(("Start")); + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + silc_say(cmd->client, "Cannot set nickname: %s", + silc_client_command_status_message(status)); + goto out; + } + + argc = silc_command_get_arg_num(cmd->payload); + if (argc < 2 || argc > 2) { + silc_say(cmd->client, "Cannot set nickname: bad reply to command"); + goto out; + } + + /* Take received Client ID */ + id_string = silc_command_get_arg_type(cmd->payload, 2, NULL); + silc_client_receive_new_id(cmd->client, cmd->sock, id_string); + + /* Update nickname on screen */ + cmd->client->screen->bottom_line->nickname = win->nickname; + silc_screen_print_bottom_line(cmd->client->screen, 0); + + out: + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(list) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(topic) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(invite) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(quit) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(kill) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(info) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(away) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(connect) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(ping) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(oper) +{ +} + +/* Received reply for JOIN command. */ + +SILC_CLIENT_CMD_REPLY_FUNC(join) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClient client = cmd->client; + SilcCommandStatus status; + unsigned int argc; + unsigned char *id_string; + char *topic, *tmp, *channel_name; + + SILC_LOG_DEBUG(("Start")); + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + silc_say(cmd->client, "%s", silc_client_command_status_message(status)); + goto out; + } + + argc = silc_command_get_arg_num(cmd->payload); + if (argc < 3 || argc > 4) { + silc_say(cmd->client, "Cannot join channel: Bad reply packet"); + goto out; + } + + /* Get channel name */ + tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + channel_name = strdup(tmp); + + /* Get channel ID */ + id_string = silc_command_get_arg_type(cmd->payload, 3, NULL); + + /* Get topic */ + topic = silc_command_get_arg_type(cmd->payload, 4, NULL); + + /* Save received Channel ID */ + silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, id_string); + + /* Print channel name on screen */ + client->screen->bottom_line->channel = channel_name; + silc_screen_print_bottom_line(client->screen, 0); + + if (topic) + silc_say(client, "Topic for %s: %s", channel_name, topic); + + out: + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(motd) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(umode) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(cmode) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(kick) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(restart) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(close) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(die) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(silcoper) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(leave) +{ +} + +SILC_CLIENT_CMD_REPLY_FUNC(names) +{ +} + +/* Private message received. This processes the private message and + finally displays it on the screen. */ + +SILC_CLIENT_CMD_REPLY_FUNC(msg) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClient client = cmd->client; + SilcBuffer buffer = (SilcBuffer)cmd->context; + unsigned short nick_len; + unsigned char *nickname, *message; + SilcIDCache *id_cache; + unsigned char *id_string; + void *id; + + /* Get nickname */ + silc_buffer_unformat(buffer, + SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len), + SILC_STR_END); + silc_buffer_pull(buffer, 2 + nick_len); + +#if 0 + /* Get ID of the sender */ + id_string = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char *)); + silc_buffer_push(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN); + memcpy(id_string, buffer->data, SILC_ID_CLIENT_LEN); + silc_buffer_pull(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN); + id = silc_id_str2id(id_string, SILC_ID_CLIENT); + silc_free(id_string); + + /* Nickname should be verified if we don't have it in the cache */ + if (silc_idcache_find_by_data(client->current_win-> + client_id_cache[nickname[0] - 32], + client->current_win-> + client_id_cache_count[nickname[0] - 32], + nickname, &id_cache) == FALSE) { + + SilcClientCommandContext ctx; + char whois[255]; + + /* Private message from unknown source, try to resolve it. */ + + + return; + } +#endif + + message = silc_calloc(buffer->len + 1, sizeof(char)); + memcpy(message, buffer->data, buffer->len); + silc_print(client, "*%s* %s", nickname, message); + memset(message, 0, buffer->len); + silc_free(message); +} diff --git a/apps/silc/command_reply.h b/apps/silc/command_reply.h new file mode 100644 index 00000000..36b31286 --- /dev/null +++ b/apps/silc/command_reply.h @@ -0,0 +1,106 @@ +/* + + command_reply.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef COMMAND_REPLY_H +#define COMMAND_REPLY_H + +/* Structure holding one command reply and pointer to its function. */ +typedef struct { + SilcCommandCb cb; + SilcCommand cmd; +} SilcClientCommandReply; + +/* All client command replys */ +extern SilcClientCommandReply silc_command_reply_list[]; + +/* Context sent as argument to all command reply functions */ +typedef struct { + SilcClient client; + SilcSocketConnection sock; + SilcCommandPayload payload; + + /* If defined this executes the pending command. */ + void *context; + SilcClientCommandCallback callback; +} *SilcClientCommandReplyContext; + +/* Macros */ + +/* Macro used for command declaration in command reply list structure */ +#define SILC_CLIENT_CMD_REPLY(func, cmd ) \ +{ silc_client_command_reply_##func, SILC_COMMAND_##cmd } + +/* Macro used to declare command reply functions */ +#define SILC_CLIENT_CMD_REPLY_FUNC(func) \ +void silc_client_command_reply_##func(void *context) + +/* Macro used to execute command replies */ +#define SILC_CLIENT_COMMAND_REPLY_EXEC(ctx) \ +do { \ + SilcClientCommandReply *cmd; \ + \ + for (cmd = silc_command_reply_list; cmd->cb; cmd++) \ + if (cmd->cmd == silc_command_get(ctx->payload)) { \ + cmd->cb(ctx); \ + break; \ + } \ + \ + if (cmd == NULL) { \ + silc_free(ctx); \ + return; \ + } \ +} while(0) + +/* Prototypes */ +void silc_client_command_reply_process(SilcClient client, + SilcSocketConnection sock, + SilcBuffer buffer); +SILC_CLIENT_CMD_REPLY_FUNC(whois); +SILC_CLIENT_CMD_REPLY_FUNC(whowas); +SILC_CLIENT_CMD_REPLY_FUNC(identify); +SILC_CLIENT_CMD_REPLY_FUNC(nick); +SILC_CLIENT_CMD_REPLY_FUNC(list); +SILC_CLIENT_CMD_REPLY_FUNC(topic); +SILC_CLIENT_CMD_REPLY_FUNC(invite); +SILC_CLIENT_CMD_REPLY_FUNC(quit); +SILC_CLIENT_CMD_REPLY_FUNC(kill); +SILC_CLIENT_CMD_REPLY_FUNC(info); +SILC_CLIENT_CMD_REPLY_FUNC(links); +SILC_CLIENT_CMD_REPLY_FUNC(stats); +SILC_CLIENT_CMD_REPLY_FUNC(users); +SILC_CLIENT_CMD_REPLY_FUNC(away); +SILC_CLIENT_CMD_REPLY_FUNC(connect); +SILC_CLIENT_CMD_REPLY_FUNC(ping); +SILC_CLIENT_CMD_REPLY_FUNC(pong); +SILC_CLIENT_CMD_REPLY_FUNC(oper); +SILC_CLIENT_CMD_REPLY_FUNC(join); +SILC_CLIENT_CMD_REPLY_FUNC(motd); +SILC_CLIENT_CMD_REPLY_FUNC(umode); +SILC_CLIENT_CMD_REPLY_FUNC(cmode); +SILC_CLIENT_CMD_REPLY_FUNC(kick); +SILC_CLIENT_CMD_REPLY_FUNC(restart); +SILC_CLIENT_CMD_REPLY_FUNC(close); +SILC_CLIENT_CMD_REPLY_FUNC(die); +SILC_CLIENT_CMD_REPLY_FUNC(silcoper); +SILC_CLIENT_CMD_REPLY_FUNC(leave); +SILC_CLIENT_CMD_REPLY_FUNC(names); +SILC_CLIENT_CMD_REPLY_FUNC(msg); + +#endif diff --git a/apps/silc/idlist.h b/apps/silc/idlist.h new file mode 100644 index 00000000..9c769e98 --- /dev/null +++ b/apps/silc/idlist.h @@ -0,0 +1,58 @@ +/* + + idlist.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef IDLIST_H +#define IDLIST_H + +/* Client entry context. When client receives information about new client + (it receives its ID, for example, by IDENTIFY request) we create new + client entry. This entry also includes the private message keys if + they are used. */ +typedef struct SilcClientEntryStruct { + char *nickname; + SilcClientID *id; + + /* Keys, these are defined if private message key has been defined + with the remote client. */ + SilcCipher send_key; + SilcCipher receive_key; +} SilcClientEntryObject; + +typedef SilcClientEntryObject *SilcClientEntry; + +/* Channel entry context. This is allocate for every channel client has + joined to. This includes for example the channel specific keys */ +/* XXX channel_key is the server generated key. Later this context must + include the channel private key. */ +typedef struct SilcChannelEntryStruct { + char *channel_name; + SilcChannelID *id; + int on_channel; + + /* Channel keys */ + SilcCipher channel_key; + unsigned char *key; + unsigned int key_len; + unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; +} SilcChannelEntryObject; + +typedef SilcChannelEntryObject *SilcChannelEntry; + +#endif diff --git a/apps/silc/protocol.c b/apps/silc/protocol.c new file mode 100644 index 00000000..bfbb2aa8 --- /dev/null +++ b/apps/silc/protocol.c @@ -0,0 +1,464 @@ +/* + + protocol.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * Client side of the protocols. + */ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" + +SILC_TASK_CALLBACK(silc_client_protocol_connection_auth); +SILC_TASK_CALLBACK(silc_client_protocol_channel_auth); +SILC_TASK_CALLBACK(silc_client_protocol_key_exchange); + +/* SILC client protocol list */ +const SilcProtocolObject silc_protocol_list[] = +{ + { SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, + silc_client_protocol_connection_auth }, + { SILC_PROTOCOL_CLIENT_CHANNEL_AUTH, + silc_client_protocol_channel_auth }, + { SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, + silc_client_protocol_key_exchange }, + + { SILC_PROTOCOL_CLIENT_NONE, NULL }, +}; + +/* + * Key Exhange protocol functions + */ + +static void silc_client_protocol_ke_send_packet(SilcSKE ske, + SilcBuffer packet, + SilcPacketType type, + void *context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientKEInternalContext *ctx = + (SilcClientKEInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + + /* Send the packet immediately */ + silc_client_packet_send(client, ske->sock, type, NULL, 0, NULL, NULL, + packet->data, packet->len, TRUE); + +} + +static void silc_client_protocol_ke_phase1_cb(SilcSKE ske, + void *context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientKEInternalContext *ctx = + (SilcClientKEInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + + SILC_LOG_DEBUG(("Start")); + +} + +static void silc_client_protocol_ke_finish_cb(SilcSKE ske, + void *context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientKEInternalContext *ctx = + (SilcClientKEInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + + SILC_LOG_DEBUG(("Start")); + +} + +/* Sets the negotiated key material into use for particular connection. */ + +static void silc_client_protocol_ke_set_keys(SilcSKE ske, + SilcSocketConnection sock, + SilcSKEKeyMaterial *keymat, + SilcCipher cipher, + SilcPKCS pkcs, + SilcHash hash) +{ + SilcClientWindow win = (SilcClientWindow)sock->user_data; + SilcHash nhash; + + SILC_LOG_DEBUG(("Setting new keys into use")); + + /* Allocate cipher to be used in the communication */ + silc_cipher_alloc(cipher->cipher->name, &win->send_key); + silc_cipher_alloc(cipher->cipher->name, &win->receive_key); + + win->send_key->cipher->set_key(win->send_key->context, + keymat->send_enc_key, + keymat->enc_key_len); + win->send_key->set_iv(win->send_key, keymat->send_iv); + win->receive_key->cipher->set_key(win->receive_key->context, + keymat->receive_enc_key, + keymat->enc_key_len); + win->receive_key->set_iv(win->receive_key, keymat->receive_iv); + + /* Allocate PKCS to be used */ +#if 0 + /* XXX Do we ever need to allocate PKCS for the connection?? + If yes, we need to change KE protocol to get the initiators + public key. */ + silc_pkcs_alloc(pkcs->pkcs->name, &win->public_Key); + silc_pkcs_set_public_key(win->public_key, ske->ke2_payload->pk_data, + ske->ke2_payload->pk_len); +#endif + + /* Save HMAC key to be used in the communication. */ + silc_hash_alloc(hash->hash->name, &nhash); + silc_hmac_alloc(nhash, &win->hmac); + win->hmac_key_len = keymat->hmac_key_len; + win->hmac_key = silc_calloc(win->hmac_key_len, + sizeof(unsigned char)); + memcpy(win->hmac_key, keymat->hmac_key, keymat->hmac_key_len); + +} + +/* Performs key exchange protocol. This is used for both initiator + and responder key exchange. This may be called recursively. */ + +SILC_TASK_CALLBACK(silc_client_protocol_key_exchange) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientKEInternalContext *ctx = + (SilcClientKEInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + SilcSKEStatus status; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) + protocol->state = SILC_PROTOCOL_STATE_START; + + switch(protocol->state) { + case SILC_PROTOCOL_STATE_START: + { + /* + * Start Protocol + */ + SilcSKE ske; + + /* Allocate Key Exchange object */ + ske = silc_ske_alloc(); + ctx->ske = ske; + + if (ctx->responder == TRUE) { +#if 0 + SilcBuffer start_payload; + + + /* Start the key exchange by processing the received security + properties packet from initiator. */ + status = silc_ske_responder_start(ske, ctx->rng, ctx->sock, + start_payload, + silc_client_protocol_ke_send_packet, + context); +#endif + } else { + SilcSKEStartPayload *start_payload; + + /* Assemble security properties. */ + silc_ske_assemble_security_properties(ske, &start_payload); + + /* Start the key exchange by sending our security properties + to the remote end. */ + status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock, + start_payload, + silc_client_protocol_ke_send_packet, + context); + } + + if (status != SILC_SKE_STATUS_OK) { + switch(status) { + + default: + break; + } + } + + /* Advance the state of the protocol. */ + protocol->state++; + } + break; + case 2: + { + /* + * Phase 1 + */ + if (ctx->responder == TRUE) { +#if 0 + status = + silc_ske_responder_phase_1(ctx->ske, + ctx->ske->start_payload, + silc_server_protocol_ke_send_packet, + context); +#endif + } else { + /* 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 + save it. */ + status = + silc_ske_initiator_phase_1(ctx->ske, + ctx->packet, + silc_client_protocol_ke_phase1_cb, + context); + } + + switch(status) { + default: + break; + } + + /* Advance the state of the protocol and call the next state. */ + protocol->state++; + protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0); + } + break; + case 3: + { + /* + * Phase 2 + */ + if (ctx->responder == TRUE) { +#if 0 + status = + silc_ske_responder_phase_2(ctx->ske, + ctx->ske->start_payload, + silc_server_protocol_ke_send_packet, + context); +#endif + } else { + /* Call the Phase-2 function. This creates Diffie Hellman + key exchange parameters and sends our public part inside + Key Exhange 1 Payload to the responder. */ + status = + silc_ske_initiator_phase_2(ctx->ske, + silc_client_protocol_ke_send_packet, + context); + } + + switch(status) { + default: + break; + } + + /* Advance the state of the protocol. */ + protocol->state++; + } + break; + case 4: + { + /* + * Finish protocol + */ + if (ctx->responder == TRUE) { +#if 0 + status = + silc_ske_responder_phase_2(ctx->ske, + ctx->ske->start_payload, + silc_server_protocol_ke_send_packet, + context); +#endif + } else { + /* Finish the protocol. This verifies the Key Exchange 2 payload + sent by responder. */ + status = + silc_ske_initiator_finish(ctx->ske, + ctx->packet, + silc_client_protocol_ke_finish_cb, + context); + } + + switch(status) { + default: + break; + } + + /* Send Ok to the other end. We will end the protocol as server + sends Ok to us when we will take the new keys into use. */ + silc_ske_end(ctx->ske, silc_client_protocol_ke_send_packet, context); + + /* End the protocol on the next round */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + break; + case SILC_PROTOCOL_STATE_END: + { + /* + * End protocol + */ + SilcSKEKeyMaterial *keymat; + + /* Process the key material */ + keymat = silc_calloc(1, sizeof(*keymat)); + silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat); + + /* Take the negotiated keys into use. */ + silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat, + ctx->ske->prop->cipher, + ctx->ske->prop->pkcs, + ctx->ske->prop->hash); + + /* Protocol has ended, call the final callback */ + if (protocol->final_callback) + protocol->execute_final(client->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + } + break; + case SILC_PROTOCOL_STATE_ERROR: + + /* On error the final callback is always called. */ + /* protocol->final_callback(pptr, context);*/ + break; + case SILC_PROTOCOL_STATE_UNKNOWN: + break; + } +} + +/* + * Connection Authentication protocol functions + */ + +SILC_TASK_CALLBACK(silc_client_protocol_connection_auth) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcClientConnAuthInternalContext *ctx = + (SilcClientConnAuthInternalContext *)protocol->context; + SilcClient client = (SilcClient)ctx->client; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) + protocol->state = SILC_PROTOCOL_STATE_START; + + switch(protocol->state) { + case SILC_PROTOCOL_STATE_START: + { + /* + * Start protocol. We send authentication data to the server + * to be authenticated. + */ + SilcBuffer packet; + int payload_len = 0; + unsigned char *auth_data = NULL; + unsigned int auth_data_len = 0; + + switch(ctx->auth_meth) { + case SILC_PROTOCOL_CONN_AUTH_NONE: + /* No authentication required */ + break; + + case SILC_PROTOCOL_CONN_AUTH_PASSWORD: + /* Password authentication */ + if (ctx->auth_data && ctx->auth_data_len) { + auth_data = ctx->auth_data; + auth_data_len = ctx->auth_data_len; + break; + } + + silc_say(client, "Password authentication required by server %s", + ctx->sock->hostname); + auth_data = silc_client_ask_passphrase(client); + auth_data_len = strlen(auth_data); + break; + + case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: +#if 0 + +#endif + break; + } + + payload_len = 4 + auth_data_len; + packet = silc_buffer_alloc(payload_len); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(payload_len), + SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT), + SILC_STR_UI_XNSTRING(auth_data, auth_data_len), + SILC_STR_END); + + /* Send the packet to server */ + silc_client_packet_send(client, ctx->sock, + SILC_PACKET_CONNECTION_AUTH, + NULL, 0, NULL, NULL, + packet->data, packet->len, TRUE); + + if (auth_data) { + memset(auth_data, 0, auth_data_len); + silc_free(auth_data); + } + silc_buffer_free(packet); + + /* Next state is end of protocol */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + break; + + case SILC_PROTOCOL_STATE_END: + { + /* + * End protocol. Nothing special to be done here. + */ + + /* Protocol has ended, call the final callback */ + if (protocol->final_callback) + protocol->execute_final(client->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + } + break; + + case SILC_PROTOCOL_STATE_ERROR: + { + /* + * Error + */ + + /* Error in protocol. Send FAILURE packet. Although I don't think + this could ever happen on client side. */ + silc_client_packet_send(client, ctx->sock, SILC_PACKET_FAILURE, + NULL, 0, NULL, NULL, NULL, 0, TRUE); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + protocol->execute_final(client->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + } + break; + case SILC_PROTOCOL_STATE_UNKNOWN: + break; + } +} + +SILC_TASK_CALLBACK(silc_client_protocol_channel_auth) +{ +} diff --git a/apps/silc/protocol.h b/apps/silc/protocol.h new file mode 100644 index 00000000..1c40ef38 --- /dev/null +++ b/apps/silc/protocol.h @@ -0,0 +1,64 @@ +/* + + protocol.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef PROTOCOL_H +#define PROTOCOL_H + +/* SILC client protocol types */ +#define SILC_PROTOCOL_CLIENT_NONE 0 +#define SILC_PROTOCOL_CLIENT_CONNECTION_AUTH 1 +#define SILC_PROTOCOL_CLIENT_CHANNEL_AUTH 2 +#define SILC_PROTOCOL_CLIENT_KEY_EXCHANGE 3 +/* #define SILC_PROTOCOL_CLIENT_MAX 255 */ + +/* Internal context for key exchange protocol */ +typedef struct { + void *client; + SilcSocketConnection sock; + SilcRng rng; + int responder; + SilcBuffer packet; + SilcSKE ske; +} SilcClientKEInternalContext; + +/* Internal context for connection authentication protocol */ +typedef struct { + void *client; + SilcSocketConnection sock; + + /* SKE object from Key Exchange protocol. */ + SilcSKE ske; + + /* Auth method that must be used. This is resolved before this + connection authentication protocol is started. */ + unsigned int auth_meth; + + /* Authentication data if we alreay know it. This is filled before + starting the protocol if we know the authentication data. Otherwise + these are and remain NULL. */ + unsigned char *auth_data; + unsigned int auth_data_len; + + SilcTask timeout_task; +} SilcClientConnAuthInternalContext; + +/* Prototypes */ + +#endif diff --git a/apps/silc/pubkey.pub b/apps/silc/pubkey.pub new file mode 100644 index 0000000000000000000000000000000000000000..7dc0bfd07b03d28db987547ee9a4a35d40c84af3 GIT binary patch literal 330 zcmZvYOG<-55QL2j7db^P5a{=GZxe$ALlDG;%bw|(1L*z1U)L7Ys)~=Ij>k=>%ggnC zc)#AxhqsT@?Rq}EUOs<=Cik z&j4Ae7V6bIXNFY-sI7zzGguJYY~2cD^#(+N3&KE&fiz9HwVYemN5EVG#)@k=Br2db zVW9wMLKRxo#^#MilWj~cwW+Riae;70an?m#u1>I^q=q|?7%)zBamm)JSrEv?OzhJ) TWGZcY4Wjmck6iB0|3CEuYXMV< literal 0 HcmV?d00001 diff --git a/apps/silc/screen.c b/apps/silc/screen.c new file mode 100644 index 00000000..10a52f06 --- /dev/null +++ b/apps/silc/screen.c @@ -0,0 +1,561 @@ +/* + + screen.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * SILC client screen routines. These implement the user interface + * on ncurses routines. Most of these routines were taken from the + * old version of the SILC client dating back to 1997. + */ +/* XXX: Input line handling is really buggy! */ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" + +SilcScreen silc_screen_init() +{ + SilcScreen new; + + new = silc_malloc(sizeof(*new)); + if (new == NULL) { + SILC_LOG_ERROR(("Could not create new screen object")); + return NULL; + } + + new->output_win_count = 0; + new->input_pos = 0; + new->cursor_pos = 0; + new->virtual_window = 0; + new->insert = TRUE; + + initscr(); + cbreak(); + nonl(); + noecho(); + + silc_screen_create_output_window(new); + silc_screen_create_input_window(new); + + return new; +} + +/* Creates one (main) output window. Returns new created physical + window. */ + +WINDOW *silc_screen_create_output_window(SilcScreen screen) +{ + assert(screen != NULL); + + screen->output_win = silc_malloc(sizeof(*screen->output_win) * 1); + screen->output_win_count = 1; + screen->output_win[0] = newwin(LINES - 3, COLS, 1, 0); + scrollok(screen->output_win[0], TRUE); + idlok(screen->output_win[0], TRUE); + wrefresh(screen->output_win[0]); + + return screen->output_win[0]; +} + +/* Adds new output window. Return new created physical window. */ + +WINDOW *silc_screen_add_output_window(SilcScreen screen) +{ + int i; + + assert(screen != NULL); + + screen->output_win = silc_realloc(screen->output_win, + (screen->output_win_count + 1) * + sizeof(*screen->output_win)); + i = screen->output_win_count; + screen->output_win[i] = newwin(LINES - 3, COLS, 1, 0); + scrollok(screen->output_win[i], TRUE); + idlok(screen->output_win[i], TRUE); + wrefresh(screen->output_win[i]); + screen->output_win_count++; + + return screen->output_win[i]; +} + +void silc_screen_create_input_window(SilcScreen screen) +{ + assert(screen != NULL); + + screen->input_win = newwin(0, COLS, LINES - 1, 0); + scrollok(screen->input_win, TRUE); + keypad(screen->input_win, TRUE); + wrefresh(screen->input_win); +} + +void silc_screen_init_upper_status_line(SilcScreen screen) +{ + int i; + int justify; + + assert(screen != NULL); + + /* Create upper status line */ + screen->upper_stat_line = newwin(0, COLS, 0, 0); + scrollok(screen->upper_stat_line, FALSE); + wattrset(screen->upper_stat_line, A_REVERSE); + + /* Print empty line */ + for (i = 0; i < COLS - 1; i++) + mvwprintw(screen->upper_stat_line, 0, i, " "); + + /* Print stuff with justify */ + justify = COLS / 5; + mvwprintw(screen->upper_stat_line, 0, 1, "%s %s", + screen->u_stat_line.program_name, + screen->u_stat_line.program_version); + /* + mvwprintw(screen->upper_stat_line, 0, justify, "[Your Connection: %s]", + stat.uconnect_status[stat.uconnect]); + mvwprintw(screen->upper_stat_line, 0, + (justify + justify + justify), "[SILC: %s]", + stat.silc_status[stat.silc]); + */ + + /* Prints clock on upper stat line */ + silc_screen_print_clock(screen); + wrefresh(screen->upper_stat_line); +} + +void silc_screen_init_output_status_line(SilcScreen screen) +{ + int i; + + assert(screen != NULL); + + screen->output_stat_line = silc_calloc(1, sizeof(*screen->output_stat_line)); + + screen->output_stat_line[0] = newwin(1, COLS, LINES - 2, 0); + scrollok(screen->output_stat_line[0], FALSE); + wattrset(screen->output_stat_line[0], A_REVERSE); + + /* print first just blank line */ + for (i = 0; i < COLS - 1; i++) + mvwprintw(screen->output_stat_line[0], 0, i, " "); + + /* Allocate bottom line */ + screen->bottom_line = silc_calloc(1, sizeof(*screen->bottom_line)); + + wattrset(screen->output_stat_line[0], A_NORMAL); + wrefresh(screen->output_stat_line[0]); +} + +void silc_screen_print_clock(SilcScreen screen) +{ + time_t curtime; + struct tm *tp; + + curtime = time(0); + tp = localtime(&curtime); + + mvwprintw(screen->upper_stat_line, 0, COLS - 8, "[%02d:%02d] ", + tp->tm_hour, tp->tm_min); + wrefresh(screen->upper_stat_line); +} + +/* Prints current cursor coordinates on some output stat line */ + +void silc_screen_print_coordinates(SilcScreen screen, int win_index) +{ + wattrset(screen->output_stat_line[win_index], A_REVERSE); + mvwprintw(screen->output_stat_line[win_index], 0, COLS - 10, + "[%4d,%3d]", screen->input_pos, LINES); + wrefresh(screen->output_stat_line[win_index]); + wattrset(screen->output_stat_line[win_index], A_NORMAL); +} + +/* Prints bottom line (the status line) of the screen. */ + +void silc_screen_print_bottom_line(SilcScreen screen, int win_index) +{ + char buf[512]; + SilcScreenBottomLine line = screen->bottom_line; + int i, len; + + memset(buf, 0, sizeof(buf)); + + if (line->mode) { + len = strlen(line->mode); + strncat(buf, line->mode, len); + } + + if (line->nickname) { + len = strlen(line->nickname); + strncat(buf, line->nickname, len > SILC_SCREEN_MAX_NICK_LEN ? + SILC_SCREEN_MAX_NICK_LEN : len); + } + + if (line->connection) { + len = strlen(line->connection); + strncat(buf, " via ", 5); + strncat(buf, line->connection, len > SILC_SCREEN_MAX_CONN_LEN ? + SILC_SCREEN_MAX_CONN_LEN : len); + } + + if (line->channel) { + len = strlen(line->channel); + strncat(buf, " ", 1); + strncat(buf, line->channel, len > SILC_SCREEN_MAX_CHANNEL_LEN ? + SILC_SCREEN_MAX_CHANNEL_LEN : len); + } + + wattrset(screen->output_stat_line[win_index], A_REVERSE); + + for (i = 0; i < COLS - 10; i++) + mvwprintw(screen->output_stat_line[win_index], 0, i, " "); + + mvwprintw(screen->output_stat_line[win_index], 0, 0, " %s", buf); + silc_screen_print_coordinates(screen, win_index); + wrefresh(screen->output_stat_line[win_index]); + wattrset(screen->output_stat_line[win_index], A_NORMAL); +} + +/* Refresh all windows */ + +void silc_screen_refresh_all(SilcScreen screen) +{ + int i; + + assert(screen != NULL); + + redrawwin(screen->upper_stat_line); + + for (i = 0; i < screen->output_win_count; i++) { + wrefresh(screen->output_win[i]); + redrawwin(screen->output_win[i]); + } + + wrefresh(screen->input_win); + redrawwin(screen->input_win); +} + +/* Refreshes a window */ + +void silc_screen_refresh_win(WINDOW *win) +{ + assert(win != NULL); + + redrawwin(win); + wrefresh(win); +} + +/* Resets input window */ + +void silc_screen_input_reset(SilcScreen screen) +{ + int i; + + assert(screen != NULL); + for (i = 0; i < COLS - 1; i++) + mvwprintw(screen->input_win, 0, i, " "); + mvwprintw(screen->input_win, 0, 0, ""); + wrefresh(screen->input_win); + screen->input_pos = 0; + screen->input_end = 0; + screen->cursor_pos = 0; + screen->virtual_window = 0; +} + +/* Backspace. Removes one character from input windows. */ + +void silc_screen_input_backspace(SilcScreen screen) +{ + WINDOW *win; + char *buffer; + + assert(screen != NULL); + buffer = screen->input_buffer; + win = screen->input_win; + + /* Return directly if at the start of input line */ + if (screen->input_pos == 0) + return; + + if (screen->virtual_window) { + if (screen->cursor_pos <= 10) { + int i; + + /* Clear line */ + for (i = 0; i < COLS; i++) + mvwprintw(win, 0, i, " "); + mvwprintw(win, 0, 0, ""); + + screen->virtual_window--; + + waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS); + screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5)) + 1; + screen->input_end = ((screen->virtual_window + 1) * (COLS - 5)) + 1; + screen->cursor_pos = (COLS - 5) + 1; + wrefresh(win); + } + } + + screen->cursor_pos--; + screen->input_pos--; + screen->input_end--; + mvwdelch(win, 0, screen->cursor_pos); + + if (screen->input_pos < screen->input_end) + /* Delete from inside the input line */ + SILC_SCREEN_INPUT_DELETE(buffer, screen->input_pos, screen->input_end); + else + /* Delete from the end of the input line */ + buffer[screen->input_pos] = 0; + + wrefresh(win); +} + +/* Switches insert on input window on/off */ + +void silc_screen_input_insert(SilcScreen screen) +{ + assert(screen != NULL); + + screen->insert = screen->insert == TRUE ? FALSE : TRUE; +} + +/* Moves cursor one character length to rightward */ + +void silc_screen_input_cursor_right(SilcScreen screen) +{ + WINDOW *win; + char *buffer; + + assert(screen != NULL); + buffer = screen->input_buffer; + win = screen->input_win; + + /* Return directly if we are at the end of input line */ + if (screen->cursor_pos >= SILC_SCREEN_INPUT_WIN_SIZE) + return; + + /* Make sure cursor doesn't advance over the end of the line */ + if (screen->input_pos >= screen->input_end) + return; + + /* When cursor advances enough we switch to new window and show + rest of the typed characters on the screen. */ + if (screen->cursor_pos >= (COLS - 5)) { + int i; + + /* Clear line */ + for (i = 0; i < COLS; i++) + mvwprintw(win, 0, i, " "); + mvwprintw(win, 0, 0, ""); + + waddnstr(win, &buffer[screen->input_pos - 10], + ((screen->input_pos - 10) - screen->input_end >= COLS) ? + COLS : (screen->input_pos - 10) - screen->input_end); + screen->cursor_pos = 10; + wrefresh(win); + + screen->virtual_window++; + } + + screen->cursor_pos++; + screen->input_pos++; + wmove(win, 0, screen->cursor_pos); + wrefresh(win); +} + +/* Moves cursor one character length to leftward */ + +void silc_screen_input_cursor_left(SilcScreen screen) +{ + WINDOW *win; + char *buffer; + + assert(screen != NULL); + buffer = screen->input_buffer; + win = screen->input_win; + + /* Return directly if at the start of input line */ + if (screen->input_pos == 0) + return; + + /* When cursor advances enough we switch to new window and show + rest of the typed characters on the screen. */ + if (screen->virtual_window) { + if (screen->cursor_pos <= 10) { + int i; + + /* Clear line */ + for (i = 0; i < COLS; i++) + mvwprintw(win, 0, i, " "); + mvwprintw(win, 0, 0, ""); + + screen->virtual_window--; + + waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS); + screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5)) + 1; + screen->cursor_pos = (COLS - 5) + 1; + wrefresh(win); + } + } + + screen->cursor_pos--; + screen->input_pos--; + wmove(win, 0, screen->cursor_pos); + wrefresh(win); +} + +/* Moves cursor at the very start of the input line */ + +void silc_screen_input_cursor_home(SilcScreen screen) +{ + WINDOW *win; + char *buffer; + + assert(screen != NULL); + buffer = screen->input_buffer; + win = screen->input_win; + + wclear(win); + waddnstr(win, &buffer[0], COLS); + wrefresh(win); + + screen->input_pos = 0; + screen->cursor_pos = 0; + screen->virtual_window = 0; +} + +/* Moves cursor at the very end of the input line */ + +void silc_screen_input_cursor_end(SilcScreen screen) +{ + WINDOW *win; + char *buffer; + + assert(screen != NULL); + buffer = screen->input_buffer; + win = screen->input_win; + + wclear(win); + waddnstr(win, &buffer[screen->input_end - 10], 10); + wrefresh(win); + + screen->input_pos = screen->input_end; + screen->cursor_pos = 10; + /* XXX */ + screen->virtual_window = 0; +} + +/* Prints typed character into the input window for user to see. Character + attributes must be set separately outside this function. */ + +void silc_screen_input_print(SilcScreen screen, unsigned char c) +{ + WINDOW *win; + char *buffer; + + assert(screen != NULL); + buffer = screen->input_buffer; + win = screen->input_win; + + /* Return directly if input window is full */ + if (screen->input_pos >= SILC_SCREEN_INPUT_WIN_SIZE) + return; + + /* The input window is COLS wide but one can type into it at most + SILC_SCREEN_INPUT_SIZE characters. When COLS - 5 characters is + typed the window is cleared and the cursor is moved at the tenth + character in the input window. Ten last typed character is then + showed at the start of the window. */ + if (screen->cursor_pos >= (COLS - 5)) { + int i; + + /* Clear line */ + for (i = 0; i < COLS; i++) + mvwprintw(win, 0, i, " "); + mvwprintw(win, 0, 0, ""); + + /* Show ten last typed characters from the buffer on the screen */ + waddnstr(win, &buffer[screen->input_pos - 10], 10); + screen->cursor_pos = 10; + wrefresh(win); + + screen->virtual_window++; + } + + if (screen->input_pos < screen->input_end) { + /* User moved cursor into the typed line. We are not adding + character at the end of the line anymore */ + + if (screen->insert == FALSE) { + /* Add new character somewhere inside typed line. The input + line position is not advanced since a character was replaced + by the new character. */ + waddch(win, c); + buffer[screen->input_pos] = c; + screen->cursor_pos++; + screen->input_pos++; + screen->input_end = screen->input_pos; + } else { + /* Insert new character somewhere inside typed line. Other + characters are moved forward. We must advance the input line + posititon. */ + winsch(win, c); + wmove(win, 0, screen->cursor_pos + 1); + SILC_SCREEN_INPUT_INSERT(buffer, screen->input_pos, + c, screen->input_end); + screen->cursor_pos++; + screen->input_pos++; + screen->input_end++; + } + } else { + /* Add new character at the end of input line */ + waddch(win, c); + buffer[screen->input_pos] = c; + screen->input_pos++; + screen->cursor_pos++; + screen->input_end = screen->input_pos; + } + + /* Advance the cursor position. Cursor moves one to rightward always */ + wrefresh(win); +} + +/* Prints prompt to the input window. Cursors position aftern printing + is length of the prompt. */ + +void silc_screen_input_print_prompt(SilcScreen screen, char *prompt) +{ + WINDOW *win; + + assert(screen != NULL); + win = screen->input_win; + + wclear(win); + waddnstr(win, prompt, strlen(prompt)); + wrefresh(win); + + screen->input_pos = strlen(prompt); + screen->cursor_pos = strlen(prompt); + screen->virtual_window = 0; +} diff --git a/apps/silc/screen.h b/apps/silc/screen.h new file mode 100644 index 00000000..25d083b0 --- /dev/null +++ b/apps/silc/screen.h @@ -0,0 +1,122 @@ +/* + + screen.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef SCREEN_H +#define SCREEN_H + +typedef struct { + char *mode; + char *nickname; + char *connection; + char *channel; +} *SilcScreenBottomLine; + +typedef struct { + /* Status line window top of the screen */ + WINDOW *upper_stat_line; + + /* Output windows */ + WINDOW **output_win; + WINDOW **output_stat_line; + unsigned int output_win_count; + + /* Input window at the bottom of the screen */ + WINDOW *input_win; + unsigned char *input_buffer; + unsigned int input_pos; + unsigned int input_end; + unsigned int cursor_pos; + int virtual_window; + + /* Bottom line on screen */ + SilcScreenBottomLine bottom_line; + + /* On/off flag for insert */ + int insert; + + /* XXX */ + struct upper_status_line { + char *program_name; + char *program_version; + } u_stat_line; + +} SilcScreenObject; + +typedef SilcScreenObject *SilcScreen; + +/* Size of the input window. User can type this many characters into + the window. After that no more characters may be added into the + window. */ +#define SILC_SCREEN_INPUT_WIN_SIZE 1024 + +/* Maximum length of nickaname that will be shown on the screen */ +#define SILC_SCREEN_MAX_NICK_LEN 16 + +/* Maximum length of channel name that will be shown on the screen */ +#define SILC_SCREEN_MAX_CHANNEL_LEN 20 + +/* Maximum length of connection name that will be shown on the screen */ +#define SILC_SCREEN_MAX_CONN_LEN 20 + +/* Macros */ + +/* Macro used to insert typed character into the buffer. The character + is not added at the end of the buffer but somewhere in between. */ +#define SILC_SCREEN_INPUT_INSERT(__x, __y, __ch, __end) \ +do { \ + unsigned char __tmp[SILC_SCREEN_INPUT_WIN_SIZE + 1]; \ + memcpy(__tmp, &(__x)[(__y)], (__end) - (__y)); \ + (__x)[(__y)] = __ch; \ + memcpy(&(__x)[(__y) + 1], __tmp, (__end) - (__y)); \ +} while(0) + +/* Macro used to delete character from the buffer. The character + is not removed from the end of the buffer but somewhere in between. */ +#define SILC_SCREEN_INPUT_DELETE(__x, __y, __end) \ +do { \ + unsigned char __tmp[SILC_SCREEN_INPUT_WIN_SIZE + 1]; \ + memcpy(__tmp, &(__x)[(__y) + 1], (__end)); \ + memset(&(__x)[(__y)], 0, (__end) - (__y) + 1); \ + memcpy(&(__x)[(__y)], __tmp, strlen(__tmp)); \ +} while(0) + +/* Prototypes */ +SilcScreen silc_screen_init(); +WINDOW *silc_screen_create_output_window(SilcScreen screen); +WINDOW *silc_screen_add_output_window(SilcScreen screen); +void silc_screen_create_input_window(SilcScreen screen); +void silc_screen_init_upper_status_line(SilcScreen screen); +void silc_screen_init_output_status_line(SilcScreen screen); +void silc_screen_print_clock(SilcScreen screen); +void silc_screen_print_coordinates(SilcScreen screen, int win_index); +void silc_screen_print_bottom_line(SilcScreen screen, int win_index); +void silc_screen_refresh_all(SilcScreen screen); +void silc_screen_refresh_win(WINDOW *win); +void silc_screen_input_reset(SilcScreen screen); +void silc_screen_input_backspace(SilcScreen screen); +void silc_screen_input_insert(SilcScreen screen); +void silc_screen_input_cursor_right(SilcScreen screen); +void silc_screen_input_cursor_left(SilcScreen screen); +void silc_screen_input_cursor_home(SilcScreen screen); +void silc_screen_input_cursor_end(SilcScreen screen); +void silc_screen_input_print(SilcScreen screen, unsigned char c); +void silc_screen_input_print_prompt(SilcScreen screen, char *prompt); + +#endif diff --git a/apps/silc/silc.c b/apps/silc/silc.c new file mode 100644 index 00000000..fa4c1898 --- /dev/null +++ b/apps/silc/silc.c @@ -0,0 +1,262 @@ +/* + + silc.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "clientincludes.h" +#include "version.h" + +/* Long command line options */ +static struct option long_opts[] = +{ + /* Generic options */ + { "server", 1, NULL, 's' }, + { "port", 1, NULL, 'p' }, + { "nickname", 1, NULL, 'n' }, + { "channel", 1, NULL, 'c' }, + { "cipher", 1, NULL, 'r' }, + { "public-key", 1, NULL, 'b' }, + { "private-key", 1, NULL, 'k' }, + { "config-file", 1, NULL, 'f' }, + { "no-silcrc", 0, NULL, 'q' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "list-ciphers", 0, NULL, 1 }, + { "list-hash-funcs", 0, NULL, 2 }, + { "list-pkcs", 0, NULL, 3 }, + + /* Key management options */ + { "create-key-pair", 0, NULL, 'C' }, + { "pkcs", 1, NULL, 10 }, + { "bits", 1, NULL, 11 }, + + { NULL, 0, NULL, 0 } +}; + +/* Command line option variables */ +static char *opt_server = NULL; +static int opt_port = 0; +static char *opt_nickname = NULL; +static char *opt_channel = NULL; +static char *opt_cipher = NULL; +static char *opt_public_key = NULL; +static char *opt_private_key = NULL; +static char *opt_config_file = NULL; +static int opt_no_silcrc = FALSE; + +static int opt_create_keypair = FALSE; +static char *opt_pkcs = NULL; +static int opt_bits = 0; + +/* Prints out the usage of silc client */ + +void usage() +{ + printf("\ +Usage: silc [options]\n\ +\n\ + Generic Options:\n\ + -s, --server=HOST Open connection to server HOST\n\ + -p, --port=PORT Set PORT as default port to connect\n\ + -n, --nickname=STRING Set default nickname on startup\n\ + -c, --channel=STRING Join channel on startup\n\ + -r, --cipher=CIPHER Use CIPHER as default cipher in SILC\n\ + -b, --public-key=FILE Public key used in SILC\n\ + -k, --private-key=FILE Private key used in SILC\n\ + -f, --config-file=FILE Alternate configuration file\n\ + -q, --no-silcrc Don't load ~/.silcrc on startup\n\ + -h, --help Display this help message\n\ + -V, --version Display version\n\ + --list-ciphers List supported ciphers\n\ + --list-hash-funcs List supported hash functions\n\ + --list-pkcs List supported PKCS's\n\ +\n\ + Key Management Options:\n\ + -C, --create-key-pair Create new public key pair\n\ + --pkcs=PKCS Set the PKCS of the public key pair\n\ + --bits=VALUE Set length of the public key pair\n\ +\n"); +} + +int main(int argc, char **argv) +{ + int opt, option_index = 1; + int ret; + SilcClient silc = NULL; + SilcClientConfig config = NULL; + + if (argc > 1) + { + while ((opt = + getopt_long(argc, argv, + "s:p:n:c:b:k:f:qhVC", + long_opts, &option_index)) != EOF) + { + switch(opt) + { + /* + * Generic options + */ + case 's': + if (optarg) + opt_server = strdup(optarg); + break; + case 'p': + if (optarg) + opt_port = atoi(optarg); + break; + case 'n': + if (optarg) + opt_nickname = strdup(optarg); + break; + case 'c': + if (optarg) + opt_channel = strdup(optarg); + break; + case 'r': + if (optarg) + opt_cipher = strdup(optarg); + break; + case 'b': + if (optarg) + opt_public_key = strdup(optarg); + break; + case 'k': + if (optarg) + opt_private_key = strdup(optarg); + break; + case 'f': + if (optarg) + opt_config_file = strdup(optarg); + break; + case 'q': + opt_no_silcrc = TRUE; + break; + case 'h': + usage(); + exit(0); + break; + case 'V': + printf("\ +SILC Secure Internet Live Conferencing, version %s\n", + silc_version); + printf("\ +(c) 1997 - 2000 Pekka Riikonen \n"); + exit(0); + break; + case 1: + silc_client_list_ciphers(); + exit(0); + break; + case 2: + silc_client_list_hash_funcs(); + exit(0); + break; + case 3: + silc_client_list_pkcs(); + exit(0); + break; + + /* + * Key management options + */ + case 'C': + opt_create_keypair = TRUE; + break; + case 10: + if (optarg) + opt_pkcs = strdup(optarg); + break; + case 11: + if (optarg) + opt_bits = atoi(optarg); + break; + + default: + exit(0); + break; + } + } + } + + /* Init signals */ + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_DFL); + signal(SIGALRM, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGFPE, SIG_DFL); + // signal(SIGINT, SIG_IGN); + + /* Default configuration file */ + if (!opt_config_file) + opt_config_file = strdup(SILC_CLIENT_CONFIG_FILE); + + /* Read global configuration file. */ + config = silc_client_config_alloc(opt_config_file); + if (config == NULL) + goto fail; + + if (opt_create_keypair == TRUE) { + /* Create new key pair and exit */ + silc_client_create_key_pair(opt_pkcs, opt_bits); + exit(0); + } + + /* Read local configuration file */ + + + /* Allocate new client */ + ret = silc_client_alloc(&silc); + if (ret == FALSE) + goto fail; + + /* Initialize the client */ + silc->config = config; + ret = silc_client_init(silc); + if (ret == FALSE) + goto fail; + + /* Run the client */ + silc_client_run(silc); + + /* Stop the client. This probably has been done already but it + doesn't hurt to do it here again. */ + silc_client_stop(silc); + silc_client_free(silc); + + exit(0); + + fail: + if (config) + silc_client_config_free(config); + if (silc) + silc_client_free(silc); + exit(1); +} diff --git a/apps/silc/silc.h b/apps/silc/silc.h new file mode 100644 index 00000000..baa83b34 --- /dev/null +++ b/apps/silc/silc.h @@ -0,0 +1,35 @@ +/* + + silc.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef SILC_H +#define SILC_H + +/* Default client configuration file. This can be overridden at the + compilation time. Otherwise, use default. This can be overridden on + command line as well. */ +#ifndef SILC_CLIENT_CONFIG_FILE +#define SILC_CLIENT_CONFIG_FILE "/etc/silc/silc.conf" +#endif + +/* Default user configuration file. This file is searched from users' + home directory. This may override global configuration settings. */ +#define SILC_CLIENT_HOME_CONFIG_FILE ".silcrc" + +#endif diff --git a/apps/silc/testi.conf b/apps/silc/testi.conf new file mode 100644 index 00000000..72dbaed5 --- /dev/null +++ b/apps/silc/testi.conf @@ -0,0 +1,21 @@ +[cipher] +twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16 +rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16 +mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16 +none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0 + +[hash] +md5::64:16 +sha1::64:20 + +#[pkcs] +#rsa::1024 +#dss::1024 + +[connection] +#lassi.kuo.fi.ssh.com:passwd::1333 + +[commands] +#/server lassi.kuo.fi.ssh.com:1333 +#/server lassi:1334 +#/server leevi:1333 diff --git a/apps/silc/testi2.conf b/apps/silc/testi2.conf new file mode 100644 index 00000000..e1fa600f --- /dev/null +++ b/apps/silc/testi2.conf @@ -0,0 +1,21 @@ +[cipher] +twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16 +rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16 +mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16 +none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0 + +[hash] +md5::64:16 +sha1::64:20 + +#[pkcs] +#rsa::1024 +#dss::1024 + +[connection] +#lassi.kuo.fi.ssh.com:passwd::1333 + +[commands] +#/server lassi:1333 +/server lassi:1334 +#/server leevi:1333 diff --git a/apps/silcd/Makefile.am b/apps/silcd/Makefile.am new file mode 100644 index 00000000..385b8ccd --- /dev/null +++ b/apps/silcd/Makefile.am @@ -0,0 +1,42 @@ +# +# Makefile.am +# +# Author: Pekka Riikonen +# +# Copyright (C) 2000 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +bin_PROGRAMS = silcd + +silcd_SOURCES = \ + protocol.c \ + route.c \ + server.c \ + idlist.c \ + command.c \ + command_reply.c \ + serverconfig.c \ + serverid.c \ + silcd.c \ + server_version.c + +LDADD = -L. -L.. -L../lib -lsilc + +EXTRA_DIST = *.h + +INCLUDES = -I. -I.. -I../lib/silccore -I../lib/silccrypt \ + -I../lib/silcmath -I../lib/silcske -I../lib/silcsim \ + -I../includes \ + -I../lib/silcmath/gmp-3.0.1 diff --git a/apps/silcd/command.c b/apps/silcd/command.c new file mode 100644 index 00000000..eba9c41b --- /dev/null +++ b/apps/silcd/command.c @@ -0,0 +1,943 @@ +/* + + command.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "server_internal.h" + +/* Server command list. */ +SilcServerCommand silc_command_list[] = +{ + SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(list, LIST, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER), + SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(connect, CONNECT, + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER), + SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER), + SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(restart, RESTART, + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER), + SILC_SERVER_CMD(close, CLOSE, + SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER), + SILC_SERVER_CMD(die, DIE, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER), + SILC_SERVER_CMD(silcoper, SILCOPER, + SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER), + SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG | SILC_CF_REG), + SILC_SERVER_CMD(names, NAMES, SILC_CF_LAG | SILC_CF_REG), + + { NULL, 0 }, +}; + +/* List of pending commands. */ +SilcServerCommandPending *silc_command_pending = NULL; + +/* Add new pending command to the list of pending commands. Currently + pending commands are executed from command replies, thus we can + execute any command after receiving some specific command reply. + + The argument `reply_cmd' is the command reply from where the callback + function is to be called, thus, it IS NOT the command to be executed. */ + +void silc_server_command_pending(SilcCommand reply_cmd, + SilcCommandCb callback, + void *context) +{ + SilcServerCommandPending *reply, *r; + + reply = silc_calloc(1, sizeof(*reply)); + reply->reply_cmd = reply_cmd; + reply->context = context; + reply->callback = callback; + + if (silc_command_pending == NULL) { + silc_command_pending = reply; + return; + } + + for (r = silc_command_pending; r; r = r->next) { + if (r->next == NULL) { + r->next = reply; + break; + } + } +} + +/* Deletes pending command by reply command type. */ + +void silc_server_command_pending_del(SilcCommand reply_cmd) +{ + SilcServerCommandPending *r, *tmp; + + if (silc_command_pending) { + if (silc_command_pending->reply_cmd == reply_cmd) { + silc_free(silc_command_pending); + silc_command_pending = NULL; + return; + } + + for (r = silc_command_pending; r; r = r->next) { + if (r->next && r->next->reply_cmd == reply_cmd) { + tmp = r->next; + r->next = r->next->next; + silc_free(tmp); + break; + } + } + } +} + +/* Free's the command context allocated before executing the command */ + +static void silc_server_command_free(SilcServerCommandContext cmd) +{ + if (cmd) { + silc_command_free_payload(cmd->payload); + silc_free(cmd); + } +} + +/* Sends command status message as command reply packet. */ + +static void +silc_server_command_send_status_msg(SilcServerCommandContext cmd, + SilcCommand command, + SilcCommandStatus status, + unsigned char *msg, + unsigned int msg_len) +{ + SilcBuffer sp_buf, buffer; + + SILC_LOG_DEBUG(("Sending command status %d", status)); + + sp_buf = silc_command_encode_status_payload(status, msg, msg_len); + buffer = silc_command_encode_payload_va(command, 1, + sp_buf->data, sp_buf->len); + silc_server_packet_send(cmd->server, cmd->sock, + SILC_PACKET_COMMAND_REPLY, 0, + buffer->data, buffer->len, FALSE); + silc_buffer_free(buffer); + silc_buffer_free(sp_buf); +} + +/* Sends simple status message as command reply packet */ + +static void +silc_server_command_send_status_reply(SilcServerCommandContext cmd, + SilcCommand command, + SilcCommandStatus status) +{ + SilcBuffer sp_buf, buffer; + + SILC_LOG_DEBUG(("Sending command status %d", status)); + + sp_buf = silc_command_encode_status_payload(status, NULL, 0); + buffer = silc_command_encode_payload_va(command, 1, + sp_buf->data, sp_buf->len); + silc_server_packet_send(cmd->server, cmd->sock, + SILC_PACKET_COMMAND_REPLY, 0, + buffer->data, buffer->len, FALSE); + silc_buffer_free(buffer); + silc_buffer_free(sp_buf); +} + +/* Server side of command WHOIS. Processes user's query and sends found + results as command replies back to the client. */ + +SILC_SERVER_CMD_FUNC(whois) +{ + SilcServerCommandContext cmd = (SilcServerCommandContext)context; + char *tmp, *nick = NULL, *server = NULL; + unsigned int argc, count = 0, len; + SilcClientList *entry; + SilcBuffer sp_buf, packet; + unsigned char *id_string; + + SILC_LOG_DEBUG(("Start")); + + argc = silc_command_get_arg_num(cmd->payload); + if (argc < 1) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + if (argc > 2) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_TOO_MANY_PARAMS); + goto out; + } + + /* Get the nickname@server string and parse it. */ + tmp = silc_command_get_first_arg(cmd->payload, NULL); + if (tmp) { + if (strchr(tmp, '@')) { + len = strcspn(tmp, "@"); + nick = silc_calloc(len + 1, sizeof(char)); + memcpy(nick, tmp, len); + server = silc_calloc(strlen(tmp) - len, sizeof(char)); + memcpy(server, tmp + len + 1, strlen(tmp) - len - 1); + } else { + nick = strdup(tmp); + } + } else { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get the max count of reply messages allowed */ + if (argc == 2) { + tmp = silc_command_get_next_arg(cmd->payload, NULL); + if (!tmp) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_TOO_MANY_PARAMS); + if (nick) + silc_free(nick); + if (server) + silc_free(server); + goto out; + } + count = atoi(tmp); + } + + /* Then, make the query from our local client list */ + entry = silc_idlist_find_client_by_nickname(cmd->server->local_list->clients, + nick, server); + if (!entry) { + + /* If we are normal server and are connected to a router we will + make global query from the router. */ + if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) { + + goto ok; + } + + /* If we are router then we will check our global list as well. */ + if (cmd->server->server_type == SILC_ROUTER) { + entry = + silc_idlist_find_client_by_nickname(cmd->server->global_list->clients, + nick, server); + if (!entry) { + silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_NO_SUCH_NICK, + tmp, strlen(tmp)); + goto out; + } + goto ok; + } + + silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS, + SILC_STATUS_ERR_NO_SUCH_NICK, + tmp, strlen(tmp)); + goto out; + } + + ok: + /* XXX, works only for local server info */ + + /* Send WHOIS reply */ + id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT); + tmp = silc_command_get_first_arg(cmd->payload, NULL), + sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0); + + /* XXX */ + if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) { + char nh[256], uh[256]; + SilcSocketConnection hsock; + + memset(uh, 0, sizeof(uh)); + memset(nh, 0, sizeof(nh)); + + strncat(nh, entry->nickname, strlen(entry->nickname)); + strncat(nh, "@", 1); + len = entry->router ? strlen(entry->router->server_name) : + strlen(cmd->server->server_name); + strncat(nh, entry->router ? entry->router->server_name : + cmd->server->server_name, len); + + strncat(uh, entry->username, strlen(entry->username)); + strncat(uh, "@", 1); + hsock = (SilcSocketConnection)entry->connection; + len = hsock->hostname ? strlen(hsock->hostname) : strlen(hsock->ip); + strncat(uh, hsock->hostname ? hsock->hostname : hsock->ip, len); + + /* XXX */ + if (entry->userinfo) + packet = + silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 5, + sp_buf->data, sp_buf->len, + id_string, SILC_ID_CLIENT_LEN, + nh, strlen(nh), + uh, strlen(uh), + entry->userinfo, + strlen(entry->userinfo)); + else + packet = + silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4, + sp_buf->data, sp_buf->len, + id_string, SILC_ID_CLIENT_LEN, + nh, strlen(nh), + uh, strlen(uh)); + + } else { + /* XXX */ + packet = + silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4, + sp_buf->data, sp_buf->len, + id_string, SILC_ID_CLIENT_LEN, + entry->nickname, strlen(entry->nickname), + tmp, strlen(tmp)); /* XXX */ + } + silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY, + 0, packet->data, packet->len, FALSE); + + silc_free(id_string); + silc_buffer_free(packet); + silc_free(sp_buf); + + out: + silc_server_command_free(cmd); +} + +SILC_SERVER_CMD_FUNC(whowas) +{ +} + +SILC_SERVER_CMD_FUNC(identify) +{ + SilcServerCommandContext cmd = (SilcServerCommandContext)context; + char *tmp, *nick = NULL, *server = NULL; + unsigned int argc, count = 0, len; + SilcClientList *entry; + SilcBuffer sp_buf, packet; + unsigned char *id_string; + + SILC_LOG_DEBUG(("Start")); + + argc = silc_command_get_arg_num(cmd->payload); + if (argc < 1) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + if (argc > 2) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_TOO_MANY_PARAMS); + goto out; + } + + /* Get the nickname@server string and parse it. */ + tmp = silc_command_get_first_arg(cmd->payload, NULL); + if (tmp) { + if (strchr(tmp, '@')) { + len = strcspn(tmp, "@"); + nick = silc_calloc(len + 1, sizeof(char)); + memcpy(nick, tmp, len); + server = silc_calloc(strlen(tmp) - len, sizeof(char)); + memcpy(server, tmp + len + 1, strlen(tmp) - len - 1); + } else { + nick = strdup(tmp); + } + } else { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get the max count of reply messages allowed */ + if (argc == 2) { + tmp = silc_command_get_next_arg(cmd->payload, NULL); + if (!tmp) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_TOO_MANY_PARAMS); + goto out; + } + count = atoi(tmp); + } + + /* Then, make the query from our local client list */ + entry = silc_idlist_find_client_by_hash(cmd->server->local_list->clients, + nick, cmd->server->md5hash); + if (!entry) { + + /* If we are normal server and are connected to a router we will + make global query from the router. */ + if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) { + SilcBuffer buffer = cmd->packet->buffer; + + /* Forward the received IDENTIFY command to our router */ + silc_buffer_push(buffer, buffer->data - buffer->head); + silc_server_packet_forward(cmd->server, (SilcSocketConnection) + cmd->server->id_entry->router->connection, + buffer->data, buffer->len, + TRUE); + goto out; + } + + /* If we are router then we will check our global list as well. */ + if (cmd->server->server_type == SILC_ROUTER) { + entry = + silc_idlist_find_client_by_hash(cmd->server->global_list->clients, + nick, cmd->server->md5hash); + if (!entry) { + silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_NICK, + tmp, strlen(tmp)); + goto out; + } + goto ok; + } + + silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY, + SILC_STATUS_ERR_NO_SUCH_NICK, + tmp, strlen(tmp)); + goto out; + } + + ok: + /* Send IDENTIFY reply */ + id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT); + tmp = silc_command_get_first_arg(cmd->payload, NULL); + sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0); + packet = silc_command_encode_payload_va(SILC_COMMAND_IDENTIFY, 3, + sp_buf->data, sp_buf->len, + id_string, SILC_ID_CLIENT_LEN, + nick, strlen(nick)); + if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) { + void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type); + silc_server_packet_send_dest(cmd->server, cmd->sock, + SILC_PACKET_COMMAND_REPLY, 0, + id, cmd->packet->src_id_type, + packet->data, packet->len, FALSE); + silc_free(id); + } else + silc_server_packet_send(cmd->server, cmd->sock, + SILC_PACKET_COMMAND_REPLY, 0, + packet->data, packet->len, FALSE); + + silc_free(id_string); + silc_buffer_free(packet); + silc_free(sp_buf); + + out: + if (nick) + silc_free(nick); + if (server) + silc_free(server); + silc_server_command_free(cmd); +} + +/* Checks string for bad characters and returns TRUE if they are found. */ + +static int silc_server_command_bad_chars(char *nick) +{ + if (strchr(nick, '\\')) return TRUE; + if (strchr(nick, '\"')) return TRUE; + if (strchr(nick, '´')) return TRUE; + if (strchr(nick, '`')) return TRUE; + if (strchr(nick, '\'')) return TRUE; + if (strchr(nick, '*')) return TRUE; + if (strchr(nick, '/')) return TRUE; + if (strchr(nick, '@')) return TRUE; + + return FALSE; +} + +/* Server side of command NICK. Sets nickname for user. Setting + nickname causes generation of a new client ID for the client. The + new client ID is sent to the client after changing the nickname. */ + +SILC_SERVER_CMD_FUNC(nick) +{ + SilcServerCommandContext cmd = (SilcServerCommandContext)context; + SilcClientList *id_entry = (SilcClientList *)cmd->sock->user_data; + SilcServer server = cmd->server; + SilcBuffer packet, sp_buf; + SilcClientID *new_id; + char *id_string; + char *nick; + + SILC_LOG_DEBUG(("Start")); + +#define LCC(x) server->local_list->client_cache[(x) - 32] +#define LCCC(x) server->local_list->client_cache_count[(x) - 32] + + /* Check number of arguments */ + if (silc_command_get_arg_num(cmd->payload) < 1) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Check nickname */ + nick = silc_command_get_arg_type(cmd->payload, 1, NULL); + if (silc_server_command_bad_chars(nick) == TRUE) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK, + SILC_STATUS_ERR_BAD_NICKNAME); + goto out; + } + + /* Create new Client ID */ + silc_id_create_client_id(cmd->server->id, cmd->server->rng, + cmd->server->md5hash, nick, + &new_id); + + /* Send notify about nickname change to our router. We send the new + ID and ask to replace it with the old one. */ + if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) + silc_server_send_replace_id(server, server->id_entry->router->connection, + FALSE, id_entry->id, + SILC_ID_CLIENT, SILC_ID_CLIENT_LEN, + new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN); + + /* If we are router we have to distribute the new Client ID to all + routers in SILC. */ + if (cmd->server->server_type == SILC_ROUTER && !cmd->server->standalone) + silc_server_send_replace_id(server, server->id_entry->router->connection, + TRUE, id_entry->id, + SILC_ID_CLIENT, SILC_ID_CLIENT_LEN, + new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN); + + /* Remove old cache entry */ + silc_idcache_del_by_id(LCC(id_entry->nickname[0]), + LCCC(id_entry->nickname[0]), + SILC_ID_CLIENT, id_entry->id); + + /* Free old ID */ + if (id_entry->id) { + memset(id_entry->id, 0, SILC_ID_CLIENT_LEN); + silc_free(id_entry->id); + } + + /* Save the nickname as this client is our local client */ + if (id_entry->nickname) + silc_free(id_entry->nickname); + + id_entry->nickname = strdup(nick); + id_entry->id = new_id; + + /* Update client cache */ + LCCC(nick[0]) = silc_idcache_add(&LCC(nick[0]), LCCC(nick[0]), + id_entry->nickname, SILC_ID_CLIENT, + id_entry->id, (void *)id_entry); + + /* Send the new Client ID as reply command back to client */ + id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT); + sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0); + packet = silc_command_encode_payload_va(SILC_COMMAND_NICK, 2, + sp_buf->data, sp_buf->len, + id_string, SILC_ID_CLIENT_LEN); + silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY, + 0, packet->data, packet->len, FALSE); + + silc_free(id_string); + silc_buffer_free(packet); + silc_free(sp_buf); + + out: + silc_server_command_free(cmd); +#undef LCC +#undef LCCC +} + +SILC_SERVER_CMD_FUNC(list) +{ +} + +SILC_SERVER_CMD_FUNC(topic) +{ +} + +SILC_SERVER_CMD_FUNC(invite) +{ +} + +/* Quits connection to client. This gets called if client won't + close the connection even when it has issued QUIT command. */ + +SILC_TASK_CALLBACK(silc_server_command_quit_cb) +{ + SilcServer server = (SilcServer)context; + SilcSocketConnection sock = server->sockets[fd]; + + /* Free all client specific data, such as client entry and entires + on channels this client may be on. */ + silc_server_free_sock_user_data(server, sock); + + /* Close the connection on our side */ + silc_server_close_connection(server, sock); +} + +/* Quits SILC session. This is the normal way to disconnect client. */ + +SILC_SERVER_CMD_FUNC(quit) +{ + SilcServerCommandContext cmd = (SilcServerCommandContext)context; + SilcServer server = cmd->server; + SilcSocketConnection sock = cmd->sock; + + SILC_LOG_DEBUG(("Start")); + + /* We quit the connection with little timeout */ + silc_task_register(server->timeout_queue, sock->sock, + silc_server_command_quit_cb, server, + 0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); + + silc_server_command_free(cmd); +} + +SILC_SERVER_CMD_FUNC(kill) +{ +} + +SILC_SERVER_CMD_FUNC(info) +{ +} + +SILC_SERVER_CMD_FUNC(connect) +{ +} + +SILC_SERVER_CMD_FUNC(ping) +{ +} + +SILC_SERVER_CMD_FUNC(oper) +{ +} + +typedef struct { + char *channel_name; + char *nickname; + char *username; + char *hostname; + SilcChannelList *channel; + SilcServer server; +} JoinInternalContext; + +SILC_TASK_CALLBACK(silc_server_command_join_notify) +{ + JoinInternalContext *ctx = (JoinInternalContext *)context; + + if (ctx->channel->key && ctx->channel->key_len) { + silc_server_send_notify_to_channel(ctx->server, ctx->channel, + "%s (%s@%s) has joined channel %s", + ctx->nickname, ctx->username, + ctx->hostname, ctx->channel_name); + silc_free(ctx); + } else { + silc_task_register(ctx->server->timeout_queue, fd, + silc_server_command_join_notify, context, + 0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); + } +} + +/* Server side of command JOIN. Joins client into requested channel. If + the channel does not exist it will be created. */ + +SILC_SERVER_CMD_FUNC(join) +{ + SilcServerCommandContext cmd = (SilcServerCommandContext)context; + SilcServer server = cmd->server; + SilcSocketConnection sock = cmd->sock; + SilcBuffer buffer = cmd->packet->buffer; + int argc, i, tmp_len; + char *tmp, *channel_name = NULL, *cipher = NULL, *id_string = NULL; + unsigned char *passphrase; + SilcChannelList *channel; + SilcServerID *router_id; + SilcIDCache *id_cache; + SilcBuffer packet, sp_buf; + SilcClientList *client; + + SILC_LOG_DEBUG(("Start")); + +#define LCC(x) server->local_list->channel_cache[(x) - 32] +#define LCCC(x) server->local_list->channel_cache_count[(x) - 32] + + /* Check number of parameters */ + argc = silc_command_get_arg_num(cmd->payload); + if (argc < 1) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + if (argc > 3) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_TOO_MANY_PARAMS); + goto out; + } + + /* Get channel name */ + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + if (silc_server_command_bad_chars(tmp) == TRUE) { + silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, + SILC_STATUS_ERR_BAD_CHANNEL); + goto out; + } + channel_name = strdup(tmp); + + /* Get passphrase */ + tmp = silc_command_get_arg_type(cmd->payload, 2, &tmp_len); + if (tmp) { + passphrase = silc_calloc(tmp_len, sizeof(*passphrase)); + memcpy(passphrase, tmp, tmp_len); + } + + /* Get cipher name */ + cipher = silc_command_get_arg_type(cmd->payload, 3, NULL); + + /* See if the channel exists */ + if (silc_idcache_find_by_data(LCC(channel_name[0]), LCCC(channel_name[0]), + channel_name, &id_cache) == FALSE) { + /* Channel not found */ + id_cache = NULL; + + /* If we are standalone server we don't have a router, we just create + the channel by ourselves. */ + if (server->standalone) { + router_id = server->id; + channel = silc_server_new_channel(server, router_id, + cipher, channel_name); + goto join_channel; + } + + /* No channel ID found, the channel does not exist on our server. + We send JOIN command to our router which will handle the joining + procedure (either creates the channel if it doesn't exist or + joins the client to it) - if we are normal server. */ + if (server->server_type == SILC_SERVER) { + + /* Forward the received JOIN command to the router */ + silc_buffer_push(buffer, buffer->data - buffer->head); + silc_server_packet_forward(server, (SilcSocketConnection) + server->id_entry->router->connection, + buffer->data, buffer->len, + TRUE); + + /* Add the command to be pending. It will be re-executed after + router has replied back to us. */ + cmd->pending = TRUE; + silc_server_command_pending(SILC_COMMAND_JOIN, + silc_server_command_join, context); + return; + } + } + + /* If we are router and the channel does not exist we will check our + global list for the channel. */ + if (!id_cache && server->server_type == SILC_ROUTER) { + + /* Notify all routers about the new channel in SILC network. */ + if (!server->standalone) { +#if 0 + silc_server_send_new_id(server, server->id_entry->router->connection, + TRUE, + xxx, SILC_ID_CHANNEL, SILC_ID_CHANNEL_LEN); +#endif + } + + } + + channel = (SilcChannelList *)id_cache->context; + + join_channel: + + /* XXX must check whether the client already is on the channel */ + + /* Join the client to the channel */ + i = channel->user_list_count; + channel->user_list = silc_realloc(channel->user_list, + sizeof(*channel->user_list) * (i + 1)); + channel->user_list[i].mode = SILC_CHANNEL_UMODE_NONE; + + /* If the JOIN request was forwarded to us we will make a bit slower + query to get the client pointer. Otherwise, we get the client pointer + real easy. */ + if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) { + client = (SilcClientList *)sock->user_data; + channel->user_list[i].client = client; + } else { + void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type); + client = silc_idlist_find_client_by_id(server->local_list->clients, id); + channel->user_list[i].client = client; + silc_free(id); + } + channel->user_list_count++; + + i = client->channel_count; + client->channel = silc_realloc(client->channel, + sizeof(*client->channel) * (i + 1)); + client->channel[i] = channel; + client->channel_count++; + + /* Notify router about new user on channel. If we are normal server + we send it to our router, if we are router we send it to our + primary route. */ + if (!server->standalone) { + + } + + /* Send Channel ID to the client */ + if (!cmd->pending) { + id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0); + if (!channel->topic) + packet = + silc_command_encode_payload_va(SILC_COMMAND_JOIN, 3, + sp_buf->data, sp_buf->len, + channel_name, strlen(channel_name), + id_string, SILC_ID_CHANNEL_LEN); + else + packet = + silc_command_encode_payload_va(SILC_COMMAND_JOIN, 4, + sp_buf->data, sp_buf->len, + channel_name, strlen(channel_name), + id_string, SILC_ID_CHANNEL_LEN, + channel->topic, + strlen(channel->topic)); + + if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) { + void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type); + silc_server_packet_send_dest(cmd->server, cmd->sock, + SILC_PACKET_COMMAND_REPLY, 0, + id, cmd->packet->src_id_type, + packet->data, packet->len, FALSE); + silc_free(id); + } else + silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, + packet->data, packet->len, FALSE); + + silc_buffer_free(packet); + silc_free(sp_buf); + } + + /* Send channel key to the client. Client cannot start transmitting + to the channel until we have sent the key. */ + if (!cmd->pending) { + tmp_len = strlen(channel->channel_key->cipher->name); + packet = + silc_channel_key_encode_payload(SILC_ID_CHANNEL_LEN, + id_string, tmp_len, + channel->channel_key->cipher->name, + channel->key_len, channel->key); + + silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, + packet->data, packet->len, FALSE); + silc_buffer_free(packet); + } + + if (id_string) + silc_free(id_string); + + /* Finally, send notify message to all clients on the channel about + new user on the channel. */ + if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) { + if (!cmd->pending) { + silc_server_send_notify_to_channel(server, channel, + "%s (%s@%s) has joined channel %s", + client->nickname, client->username, + sock->hostname ? sock->hostname : + sock->ip, channel_name); + } else { + /* This is pending command request. Send the notify after we have + received the key for the channel from the router. */ + JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx)); + ctx->channel_name = channel_name; + ctx->nickname = client->nickname; + ctx->username = client->username; + ctx->hostname = sock->hostname ? sock->hostname : sock->ip; + ctx->channel = channel; + ctx->server = server; + silc_task_register(server->timeout_queue, sock->sock, + silc_server_command_join_notify, ctx, + 0, 100000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); + } + } + + out: + silc_server_command_free(cmd); +#undef LCC +#undef LCCC +} + +/* Server side of command MOTD. Sends servers current "message of the + day" to the client. */ + +SILC_SERVER_CMD_FUNC(motd) +{ + + SILC_LOG_DEBUG(("Start")); + +} + +SILC_SERVER_CMD_FUNC(umode) +{ +} + +SILC_SERVER_CMD_FUNC(cmode) +{ +} + +SILC_SERVER_CMD_FUNC(kick) +{ +} + +SILC_SERVER_CMD_FUNC(restart) +{ +} + +SILC_SERVER_CMD_FUNC(close) +{ +} + +SILC_SERVER_CMD_FUNC(die) +{ +} + +SILC_SERVER_CMD_FUNC(silcoper) +{ +} + +SILC_SERVER_CMD_FUNC(leave) +{ +} + +SILC_SERVER_CMD_FUNC(names) +{ +} diff --git a/apps/silcd/command.h b/apps/silcd/command.h new file mode 100644 index 00000000..d7ed4944 --- /dev/null +++ b/apps/silcd/command.h @@ -0,0 +1,164 @@ +/* + + servercommand.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef COMMAND_H +#define COMMAND_H + +/* + Structure holding one command and pointer to its function. + + SilcCommandCb cb + + Callback function called when this command is executed. + + SilcCommand cmd + + The actual command. Defined in silccore/silccommand.h + + SilcCommandFlag flags + + Flags for the command. These set how command behaves on different + situations. + +*/ +typedef struct { + SilcCommandCb cb; + SilcCommand cmd; + SilcCommandFlag flags; +} SilcServerCommand; + +/* All server commands */ +extern SilcServerCommand silc_command_list[]; + +/* Context sent as argument to all commands */ +typedef struct { + SilcServer server; + SilcSocketConnection sock; + SilcCommandPayload payload; + SilcPacketContext *packet; + int pending; +} *SilcServerCommandContext; + +/* Structure holding pending commands. If command is pending it will be + executed after command reply has been received and executed. + Pending commands are used in cases where the original command request + had to be forwarded to router. After router replies the pending + command is re-executed. */ +typedef struct SilcServerCommandPendingStruct { + SilcCommand reply_cmd; + void *context; + SilcCommandCb callback; + + struct SilcServerCommandPendingStruct *next; +} SilcServerCommandPending; + +/* List of pending commands */ +extern SilcServerCommandPending *silc_command_pending; + +/* Macros */ + +/* Macro used for command declaration in command list structure */ +#define SILC_SERVER_CMD(func, cmd, flags) \ +{ silc_server_command_##func, SILC_COMMAND_##cmd, flags } + +/* Macro used to declare command functions */ +#define SILC_SERVER_CMD_FUNC(func) \ +void silc_server_command_##func(void *context) + +/* Macro used to execute commands */ +#define SILC_SERVER_COMMAND_EXEC(ctx) \ +do { \ + SilcServerCommand *cmd; \ + \ + for (cmd = silc_command_list; cmd->cb; cmd++) \ + if (cmd->cmd == silc_command_get(ctx->payload)) { \ + cmd->cb(ctx); \ + break; \ + } \ + \ + if (cmd == NULL) { \ + SILC_LOG_ERROR(("Unknown command, packet dropped")); \ + silc_free(ctx); \ + return; \ + } \ +} while(0) + +/* Checks for pending commands */ +#define SILC_SERVER_COMMAND_CHECK_PENDING(ctx) \ +do { \ + if (silc_command_pending) { \ + SilcServerCommandPending *r; \ + SilcCommand cmd; \ + \ + cmd = silc_command_get(payload); \ + for (r = silc_command_pending; r; r = r->next) { \ + if (r->reply_cmd == cmd) { \ + ctx->context = r->context; \ + ctx->callback = r->callback; \ + break; \ + } \ + } \ + } \ +} while(0) + +/* Executed pending command */ +#define SILC_SERVER_COMMAND_EXEC_PENDING(ctx, cmd) \ +do { \ + if (ctx->callback) { \ + (*ctx->callback)(ctx->context); \ + silc_server_command_pending_del(cmd); \ + } \ +} while(0) + +/* Prototypes */ +void silc_server_command_pending(SilcCommand reply_cmd, + SilcCommandCb callback, + void *context); +void silc_server_command_pending_del(SilcCommand reply_cmd); +SILC_SERVER_CMD_FUNC(whois); +SILC_SERVER_CMD_FUNC(whowas); +SILC_SERVER_CMD_FUNC(identify); +SILC_SERVER_CMD_FUNC(newuser); +SILC_SERVER_CMD_FUNC(nick); +SILC_SERVER_CMD_FUNC(list); +SILC_SERVER_CMD_FUNC(topic); +SILC_SERVER_CMD_FUNC(invite); +SILC_SERVER_CMD_FUNC(quit); +SILC_SERVER_CMD_FUNC(kill); +SILC_SERVER_CMD_FUNC(info); +SILC_SERVER_CMD_FUNC(connect); +SILC_SERVER_CMD_FUNC(ping); +SILC_SERVER_CMD_FUNC(oper); +SILC_SERVER_CMD_FUNC(pass); +SILC_SERVER_CMD_FUNC(admin); +SILC_SERVER_CMD_FUNC(join); +SILC_SERVER_CMD_FUNC(motd); +SILC_SERVER_CMD_FUNC(umode); +SILC_SERVER_CMD_FUNC(cmode); +SILC_SERVER_CMD_FUNC(kick); +SILC_SERVER_CMD_FUNC(ignore); +SILC_SERVER_CMD_FUNC(restart); +SILC_SERVER_CMD_FUNC(close); +SILC_SERVER_CMD_FUNC(die); +SILC_SERVER_CMD_FUNC(silcoper); +SILC_SERVER_CMD_FUNC(leave); +SILC_SERVER_CMD_FUNC(names); + +#endif diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c new file mode 100644 index 00000000..57d02607 --- /dev/null +++ b/apps/silcd/command_reply.c @@ -0,0 +1,140 @@ +/* + + command_reply.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "server_internal.h" +#include "command_reply.h" + +/* Server command reply list. Not all commands have reply function as + they are never sent as forwarded command packets by server. More + maybe added later if need appears. */ +SilcServerCommandReply silc_command_reply_list[] = +{ + SILC_SERVER_CMD_REPLY(join, JOIN), + + { NULL, 0 }, +}; + +/* Process received command reply. */ + +void silc_server_command_reply_process(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcServerCommandReplyContext ctx; + SilcCommandPayload payload; + + /* Get command reply payload from packet */ + payload = silc_command_parse_payload(buffer); + if (!payload) { + /* Silently ignore bad reply packet */ + SILC_LOG_DEBUG(("Bad command reply packet")); + return; + } + + /* Allocate command reply context. This must be free'd by the + command reply routine receiving it. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->server = server; + ctx->sock = sock; + ctx->payload = payload; + + /* Check for pending commands and mark to be exeucted */ + SILC_SERVER_COMMAND_CHECK_PENDING(ctx); + + /* Execute command reply */ + SILC_SERVER_COMMAND_REPLY_EXEC(ctx); +} + +/* Free command reply context and its internals. */ + +void silc_server_command_reply_free(SilcServerCommandReplyContext cmd) +{ + if (cmd) { + silc_command_free_payload(cmd->payload); + silc_free(cmd); + } +} + +/* Received reply for forwarded JOIN command. Router has created or joined + the client to the channel. We save some channel information locally + for future use. */ + +SILC_SERVER_CMD_REPLY_FUNC(join) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcCommandStatus status; + SilcChannelID *id; + SilcChannelList *entry; + unsigned int argc; + unsigned char *id_string; + char *channel_name, *tmp; + +#define LCC(x) server->local_list->channel_cache[(x) - 32] +#define LCCC(x) server->local_list->channel_cache_count[(x) - 32] + + SILC_LOG_DEBUG(("Start")); + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) + goto out; + + /* Get channel name */ + tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + if (!tmp) + goto out; + + /* Get channel ID */ + id_string = silc_command_get_arg_type(cmd->payload, 3, NULL); + if (!id_string) + goto out; + + channel_name = strdup(tmp); + + /* Add the channel to our local list. */ + id = silc_id_str2id(id_string, SILC_ID_CHANNEL); + silc_idlist_add_channel(&server->local_list->channels, channel_name, + SILC_CHANNEL_MODE_NONE, id, + server->id_entry->router, NULL, &entry); + LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]), + LCCC(channel_name[0]), + channel_name, SILC_ID_CHANNEL, + (void *)id, (void *)entry); + entry->global_users = TRUE; + + /* Execute pending JOIN command so that the client who originally + wanted to join the channel will be joined after all. */ + SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN); + + out: + silc_server_command_reply_free(cmd); +#undef LCC +#undef LCCC +} diff --git a/apps/silcd/command_reply.h b/apps/silcd/command_reply.h new file mode 100644 index 00000000..0e67aa28 --- /dev/null +++ b/apps/silcd/command_reply.h @@ -0,0 +1,77 @@ +/* + + command_reply.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef COMMAND_REPLY_H +#define COMMAND_REPLY_H + +/* Structure holding one command reply and pointer to its function. */ +typedef struct { + SilcCommandCb cb; + SilcCommand cmd; +} SilcServerCommandReply; + +/* All server command replys */ +extern SilcServerCommandReply silc_command_reply_list[]; + +/* Context sent as argument to all command reply functions */ +typedef struct { + SilcServer server; + SilcSocketConnection sock; + SilcCommandPayload payload; + + /* If defined this executes the pending command. */ + void *context; + SilcCommandCb callback; +} *SilcServerCommandReplyContext; + +/* Macros */ + +/* Macro used for command declaration in command reply list structure */ +#define SILC_SERVER_CMD_REPLY(func, cmd ) \ +{ silc_server_command_reply_##func, SILC_COMMAND_##cmd } + +/* Macro used to declare command reply functions */ +#define SILC_SERVER_CMD_REPLY_FUNC(func) \ +void silc_server_command_reply_##func(void *context) + +/* Macro used to execute command replies */ +#define SILC_SERVER_COMMAND_REPLY_EXEC(ctx) \ +do { \ + SilcServerCommandReply *cmd; \ + \ + for (cmd = silc_command_reply_list; cmd->cb; cmd++) \ + if (cmd->cmd == silc_command_get(ctx->payload)) { \ + cmd->cb(ctx); \ + break; \ + } \ + \ + if (cmd == NULL) { \ + silc_free(ctx); \ + return; \ + } \ +} while(0) + +/* Prototypes */ +void silc_server_command_reply_process(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer); +SILC_SERVER_CMD_REPLY_FUNC(join); + +#endif diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c new file mode 100644 index 00000000..aa8409ab --- /dev/null +++ b/apps/silcd/idlist.c @@ -0,0 +1,400 @@ +/* + + idlist.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "idlist.h" + +/* Adds a new server to the list. The pointer sent as argument is allocated + and returned. */ + +void silc_idlist_add_server(SilcServerList **list, + char *server_name, int server_type, + SilcServerID *id, SilcServerList *router, + SilcCipher send_key, SilcCipher receive_key, + SilcPKCS public_key, SilcHmac hmac, + SilcServerList **new_idlist) +{ + SilcServerList *last, *idlist; + + SILC_LOG_DEBUG(("Adding new server to id list")); + + idlist = silc_calloc(1, sizeof(*idlist)); + if (idlist == NULL) { + SILC_LOG_ERROR(("Could not allocate new server list object")); + *new_idlist = NULL; + return; + } + + /* Set the pointers */ + idlist->server_name = server_name; + idlist->server_type = server_type; + idlist->id = id; + idlist->router = router; + idlist->send_key = send_key; + idlist->receive_key = receive_key; + idlist->public_key = public_key; + idlist->hmac = hmac; + idlist->next = idlist; + idlist->prev = idlist; + + /* First on the list? */ + if (!*list) { + *list = idlist; + *new_idlist = idlist; + return; + } + + /* Add it to the list */ + last = (*list)->prev; + last->next = idlist; + (*list)->prev = idlist; + idlist->next = (*list); + idlist->prev = last; + + if (new_idlist) + *new_idlist = idlist; +} + +/* Adds a new client to the client list. This is called when new client + connection is accepted to the server. This adds all the relevant data + about the client and session with it to the list. This list is + referenced for example when sending message to the client. */ + +void silc_idlist_add_client(SilcClientList **list, char *nickname, + char *username, char *userinfo, + SilcClientID *id, SilcServerList *router, + SilcCipher send_key, SilcCipher receive_key, + SilcPKCS public_key, SilcHmac hmac, + SilcClientList **new_idlist) +{ + SilcClientList *last, *idlist; + + SILC_LOG_DEBUG(("Adding new client to id list")); + + idlist = silc_calloc(1, sizeof(*idlist)); + if (idlist == NULL) { + SILC_LOG_ERROR(("Could not allocate new client list object")); + return; + } + + /* Set the pointers */ + idlist->nickname = nickname; + idlist->username = username; + idlist->userinfo = userinfo; + idlist->id = id; + idlist->router = router; + idlist->send_key = send_key; + idlist->receive_key = receive_key; + idlist->public_key = public_key; + idlist->hmac = hmac; + idlist->next = idlist; + idlist->prev = idlist; + + /* First on the list? */ + if (!(*list)) { + *list = idlist; + if (new_idlist) + *new_idlist = idlist; + return; + } + + /* Add it to the list */ + last = (*list)->prev; + last->next = idlist; + (*list)->prev = idlist; + idlist->next = *list; + idlist->prev = last; + + if (new_idlist) + *new_idlist = idlist; +} + +/* Free client entry. This free's everything. */ + +void silc_idlist_del_client(SilcClientList **list, SilcClientList *entry) +{ + if (entry) { + if (entry->nickname) + silc_free(entry->nickname); + if (entry->username) + silc_free(entry->username); + if (entry->userinfo) + silc_free(entry->userinfo); + if (entry->id) + silc_free(entry->id); + if (entry->send_key) + silc_cipher_free(entry->send_key); + if (entry->receive_key) + silc_cipher_free(entry->receive_key); + if (entry->public_key) + silc_pkcs_free(entry->public_key); + if (entry->hmac) + silc_hmac_free(entry->hmac); + if (entry->hmac_key) { + memset(entry->hmac_key, 0, entry->hmac_key_len); + silc_free(entry->hmac_key); + } + + /* Last one in list? */ + if (*list == entry && entry->next == entry) { + *list = NULL; + silc_free(entry); + return; + } + + /* At the start of list? */ + if (*list == entry && entry->next != entry) { + *list = entry->next; + entry->next->prev = entry->prev; + entry->prev->next = *list; + silc_free(entry); + return; + } + + /* Remove from list */ + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + silc_free(entry); + return; + } +} + +SilcClientList * +silc_idlist_find_client_by_nickname(SilcClientList *list, + char *nickname, + char *server) +{ + SilcClientList *first, *entry; + + SILC_LOG_DEBUG(("Finding client by nickname")); + + if (!list) + return NULL; + + first = entry = list; + if (!strcmp(entry->nickname, nickname)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + entry = entry->next; + + while(entry != first) { + if (!strcmp(entry->nickname, nickname)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + + entry = entry->next; + } + + return NULL; +} + +SilcClientList * +silc_idlist_find_client_by_hash(SilcClientList *list, + char *nickname, SilcHash md5hash) +{ + SilcClientList *first, *entry; + unsigned char hash[16]; + + SILC_LOG_DEBUG(("Finding client by nickname hash")); + + if (!list) + return NULL; + + /* Make hash of the nickname */ + silc_hash_make(md5hash, nickname, strlen(nickname), hash); + + first = entry = list; + if (entry && !SILC_ID_COMPARE_HASH(entry->id, hash)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + entry = entry->next; + + while(entry != first) { + if (entry && !SILC_ID_COMPARE_HASH(entry->id, hash)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + + entry = entry->next; + } + + return NULL; +} + +SilcClientList * +silc_idlist_find_client_by_id(SilcClientList *list, SilcClientID *id) +{ + SilcClientList *first, *entry; + + SILC_LOG_DEBUG(("Finding client by Client ID")); + + if (!list) + return NULL; + + first = entry = list; + if (entry && !SILC_ID_CLIENT_COMPARE(entry->id, id)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + entry = entry->next; + + while(entry != first) { + if (entry && !SILC_ID_CLIENT_COMPARE(entry->id, id)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + + entry = entry->next; + } + + return NULL; +} + +/* Adds new channel to the list. */ + +void silc_idlist_add_channel(SilcChannelList **list, + char *channel_name, int mode, + SilcChannelID *id, SilcServerList *router, + SilcCipher channel_key, + SilcChannelList **new_idlist) +{ + SilcChannelList *last, *idlist; + + SILC_LOG_DEBUG(("Adding new channel to id list")); + + idlist = silc_calloc(1, sizeof(*idlist)); + if (idlist == NULL) { + SILC_LOG_ERROR(("Could not allocate new channel list object")); + return; + } + + /* Set the pointers */ + idlist->channel_name = channel_name; + idlist->mode = mode; + idlist->id = id; + idlist->router = router; + idlist->channel_key = channel_key; + idlist->next = idlist; + idlist->prev = idlist; + + /* First on the list? */ + if (!*list) { + *list = idlist; + if (new_idlist) + *new_idlist = idlist; + return; + } + + /* Add it to the list */ + last = (*list)->prev; + last->next = idlist; + (*list)->prev = idlist; + idlist->next = (*list); + idlist->prev = last; + + if (new_idlist) + *new_idlist = idlist; +} + +SilcChannelList * +silc_idlist_find_channel_by_id(SilcChannelList *list, SilcChannelID *id) +{ + SilcChannelList *first, *entry; + + SILC_LOG_DEBUG(("Finding channel by Channel ID")); + + if (!list) + return NULL; + + first = entry = list; + if (entry && !SILC_ID_CHANNEL_COMPARE(entry->id, id)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + entry = entry->next; + + while(entry != first) { + if (entry && !SILC_ID_CHANNEL_COMPARE(entry->id, id)) { + SILC_LOG_DEBUG(("Found")); + return entry; + } + + entry = entry->next; + } + + return NULL; +} + +/* Free channel entry. This free's everything. */ + +void silc_idlist_del_channel(SilcChannelList **list, SilcChannelList *entry) +{ + if (entry) { + if (entry->channel_name) + silc_free(entry->channel_name); + if (entry->id) + silc_free(entry->id); + if (entry->topic) + silc_free(entry->topic); + if (entry->channel_key) + silc_cipher_free(entry->channel_key); + if (entry->key) { + memset(entry->key, 0, entry->key_len); + silc_free(entry->key); + } + memset(entry->iv, 0, sizeof(entry->iv)); + + if (entry->user_list_count) + silc_free(entry->user_list); + + /* Last one in list? */ + if (*list == entry && entry->next == entry) { + *list = NULL; + silc_free(entry); + return; + } + + /* At the start of list? */ + if (*list == entry && entry->next != entry) { + *list = entry->next; + entry->next->prev = entry->prev; + entry->prev->next = *list; + silc_free(entry); + return; + } + + /* Remove from list */ + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + silc_free(entry); + return; + } +} diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h new file mode 100644 index 00000000..b18b90dd --- /dev/null +++ b/apps/silcd/idlist.h @@ -0,0 +1,428 @@ +/* + + idlist.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef IDLIST_H +#define IDLIST_H + +/* Forward declarations */ +typedef struct SilcServerListStruct SilcServerList; +typedef struct SilcClientListStruct SilcClientList; +typedef struct SilcChannelListStruct SilcChannelList; + +/* + SILC Server list object. + + This list holds information about servers in SILC network. However, + contents of this list is highly dependent of what kind of server we are + (normal server or router server) and whether the list is used as a local + list or a global list. These factors dictates the contents of this list. + + This list is defined as follows: + + Server type List type Contents + ======================================================================= + server local list Server itself + server global list NULL + router local list All servers is the cell + router global list All servers in the SILC network + + Following short description of the fields: + + char *server_name + + Logical name of the server. There is no limit of the length of the + server name. This is usually the same name as defined in DNS. + + int server_type + + Type of the server. SILC_SERVER or SILC_ROUTER are the possible + choices for this. + + SilcServerID *id + + ID of the server. This includes all the relevant information about + the server SILC will ever need. These are also the informations + that is broadcasted between servers and routers in the SILC network. + + struct SilcServerListStruct *router + + This is a pointer back to the server list. This is the router server + where this server is connected to. If this is the router itself and + it doesn't have a route this is NULL. + + SilcCipher send_key + + SilcCipher receive_key + + void *connection + + A pointer, usually, to the socket list for fast referencing to + the data used in connection with this server. This may be anything + but as just said, this is usually pointer to the socket connection + list. + +*/ +struct SilcServerListStruct { + char *server_name; + int server_type; + SilcServerID *id; + + /* Pointer to the router */ + struct SilcServerListStruct *router; + + /* Keys */ + SilcCipher send_key; + SilcCipher receive_key; + SilcPKCS public_key; + SilcHmac hmac; + unsigned char *hmac_key; + unsigned int hmac_key_len; + + /* Connection data */ + void *connection; + + struct SilcServerListStruct *next; + struct SilcServerListStruct *prev; +}; + +/* + SILC Client list object. + + This list holds information about connected clients ie. users in the SILC + network. The contents of this list is depended on whether we are normal + server or router server and whether the list is a local or global list. + + This list is defined as follows: + + Server type List type Contents + ======================================================================= + server local list All clients in server + server global list NULL + router local list All clients in cell + router global list All clients in SILC + + Following short description of the fields: + + char username + + Client's (meaning user's) real name. This is defined in following + manner: + + Server type List type Contents + ==================================================== + server local list User's name + router local list NULL + router global list NULL + + Router doesn't hold this information since it is not vital data + for the router. If this information is needed by the client it is + fetched when it is needed. + + char userinfo + + Information about user. This is free information and can be virtually + anything. This is defined in following manner: + + Server type List type Contents + ==================================================== + server local list User's information + router local list NULL + router global list NULL + + Router doesn't hold this information since it is not vital data + for the router. If this information is needed by the client it is + fetched when it is needed. + + SilcClientID *id + + ID of the client. This includes all the information SILC will ever + need. Notice that no nickname of the user is saved anywhere. This is + beacuse of SilcClientID includes 88 bit hash value of the user's + nickname which can be used to track down specific user by their + nickname. Nickname is not relevant information that would need to be + saved as plain. + + int mode + + Client's mode. Client maybe for example server operator or + router operator (SILC operator). + + SilcServerList *router + + This is a pointer to the server list. This is the router server whose + cell this client is coming from. This is used to route messages to + this client. + + SilcCipher session_key + + The actual session key established by key exchange protcol between + connecting parties. This is used for both encryption and decryption. + + SilcPKCS public_key + + Public key of the client. This maybe NULL. + + SilcHmac hmac + unsigned char *hmac_key + unsigned int hmac_key_len + + MAC key used to compute MAC's for packets. + + void *connection + + A pointer, usually, to the socket list for fast referencing to + the data used in connection with this client. This may be anything + but as just said, this is usually pointer to the socket connection + list. + +*/ +struct SilcClientListStruct { + char *nickname; + char *username; + char *userinfo; + SilcClientID *id; + int mode; + + /* Pointer to the router */ + SilcServerList *router; + + /* Pointers to channels this client has joined */ + SilcChannelList **channel; + unsigned int channel_count; + + /* Keys */ + SilcCipher send_key; + SilcCipher receive_key; + SilcPKCS public_key; + SilcHmac hmac; + unsigned char *hmac_key; + unsigned int hmac_key_len; + + /* Connection data */ + void *connection; + + struct SilcClientListStruct *next; + struct SilcClientListStruct *prev; +}; + +/* + SILC Channel Client list structure. + + This list used only by the SilcChannelList object and it holds information + about current clients (ie. users) on channel. Following short description + of the fields: + + SilcClientList client + + Pointer to the client list. This is the client currently on channel. + + int mode + + Client's current mode on the channel. + +*/ +typedef struct SilcChannelClientListStruct { + SilcClientList *client; + int mode; +} SilcChannelClientList; + +/* + SILC Channel list object. + + This list holds information about channels in SILC network. The contents + of this list is depended on whether we are normal server or router server + and whether the list is a local or global list. + + This list is defined as follows: + + Server type List type Contents + ======================================================================= + server local list All channels in server + server global list NULL + router local list All channels in cell + router global list All channels in SILC + + Following short description of the fields: + + char *channel_name + + Logical name of the channel. + + int mode + + Current mode of the channel. + + SilcChannelID *id + + ID of the channel. This includes all the information SILC will ever + need. + + int global_users + + Boolean value to tell whether there are users outside this server + on this channel. This is set to TRUE if router sends message to + the server that there are users outside your server on your + channel as well. This way server knows that messages needs to be + sent to the router for further routing. If this is a normal + server and this channel is not created on this server this field + is always TRUE. If this server is a router this field is ignored. + + char *topic + + Current topic of the channel. + + SilcServerList *router + + This is a pointer to the server list. This is the router server + whose cell this channel belongs to. This is used to route messages + to this channel. + + SilcCipher send_key + + + SilcCipher receive_key + +*/ +struct SilcChannelListStruct { + char *channel_name; + int mode; + SilcChannelID *id; + int global_users; + char *topic; + + /* List of users on channel */ + SilcChannelClientList *user_list; + unsigned int user_list_count; + + /* Pointer to the router */ + SilcServerList *router; + + /* Channel keys */ + SilcCipher channel_key; + unsigned char *key; + unsigned int key_len; + unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; + + struct SilcChannelListStruct *next; + struct SilcChannelListStruct *prev; +}; + +/* + SILC ID List object. + + As for remainder these lists are defined as follows: + + List Server type List type Contents + ======================================================================= + servers server local list Server itself + servers server global list NULL + servers router local list All servers in cell + servers router global list All servers in SILC + + clients server local list All clients in server + clients server global list NULL + clients router local list All clients in cell + clients router global list All clients in SILC + + channels server local list All channels in server + channels server global list NULL + channels router local list All channels in cell + channels router global list All channels in SILC + + As seen on the list normal server never defines a global list. This is + because of normal server don't know anything about anything global data, + they get it from the router if and when they need it. Routers, on the + other hand, always define local and global lists because routers really + know all the relevant data in the SILC network. + +*/ +typedef struct SilcIDListStruct { + SilcServerList *servers; + SilcClientList *clients; + SilcChannelList *channels; + + /* ID Caches. Caches are used to perform fast search on the ID's. */ + SilcIDCache *server_cache[96]; + unsigned int server_cache_count[96]; + SilcIDCache *client_cache[96]; + unsigned int client_cache_count[96]; + SilcIDCache *channel_cache[96]; + unsigned int channel_cache_count[96]; +} SilcIDListObject; + +typedef SilcIDListObject *SilcIDList; + +/* + Temporary ID List object. + + This is used during authentication phases where we still don't + know what kind of connection remote connection is, hence, we + will use this structure instead until we know what type of + connection remote end is. + + This is not in any list. This is always individually allocated + and used as such. + +*/ +typedef struct { + SilcCipher send_key; + SilcCipher receive_key; + SilcPKCS pkcs; + + SilcHmac hmac; + unsigned char *hmac_key; + unsigned int hmac_key_len; + + /* SilcComp comp */ +} SilcIDListUnknown; + +/* Prototypes */ +void silc_idlist_add_server(SilcServerList **list, + char *server_name, int server_type, + SilcServerID *id, SilcServerList *router, + SilcCipher send_key, SilcCipher receive_key, + SilcPKCS public_key, SilcHmac hmac, + SilcServerList **new_idlist); +void silc_idlist_add_client(SilcClientList **list, char *nickname, + char *username, char *userinfo, + SilcClientID *id, SilcServerList *router, + SilcCipher send_key, SilcCipher receive_key, + SilcPKCS public_key, SilcHmac hmac, + SilcClientList **new_idlist); +void silc_idlist_del_client(SilcClientList **list, SilcClientList *entry); +SilcClientList * +silc_idlist_find_client_by_nickname(SilcClientList *list, + char *nickname, + char *server); +SilcClientList * +silc_idlist_find_client_by_hash(SilcClientList *list, + char *nickname, SilcHash hash); +SilcClientList * +silc_idlist_find_client_by_id(SilcClientList *list, SilcClientID *id); +void silc_idlist_add_channel(SilcChannelList **list, + char *channel_name, int mode, + SilcChannelID *id, SilcServerList *router, + SilcCipher channel_key, + SilcChannelList **new_idlist); +SilcChannelList * +silc_idlist_find_channel_by_id(SilcChannelList *list, SilcChannelID *id); +void silc_idlist_del_channel(SilcChannelList **list, SilcChannelList *entry); + +#endif diff --git a/apps/silcd/leevi.conf b/apps/silcd/leevi.conf new file mode 100644 index 00000000..d94631f7 --- /dev/null +++ b/apps/silcd/leevi.conf @@ -0,0 +1,47 @@ +[Cipher] +twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16 +rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16 +mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16 +none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0 + +[HashFunction] +md5::64:16 +sha1::64:20 + +#[PKCS] +#rsa::1024 +#dss::1024 + +[ServerInfo] +leevi.kuo.fi.ssh.com:10.2.1.7:Kuopio, Finland:1333 + +[AdminInfo] +Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi + +[ListenPort] +10.2.1.7:10.2.1.7:1333 + +[Logging] +infologfile:silcd.log:10000 +#warninglogfile:/var/log/silcd_warning.log:10000 +errorlogfile:leevi_error.log:10000 +#fatallogfile:/var/log/silcd_error.log: + +[ConnectionClass] +1:100:100:100 +2:200:300:400 + +[ClientConnection] +10.2.1.199:passwd:priikone:333:1 +:::1333:1 + +[AdminConnection] +10.2.1.199:passwd:priikone:priikone:1 + +[ServerConnection] +10.2.1.7:passwd:priikone:1334:1:1 + +[RouterConnection] + +[DenyConnection] +[RedirectClient] diff --git a/apps/silcd/leevi2.conf b/apps/silcd/leevi2.conf new file mode 100644 index 00000000..4368321f --- /dev/null +++ b/apps/silcd/leevi2.conf @@ -0,0 +1,47 @@ +[Cipher] +twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16 +rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16 +mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16 +none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0 + +[HashFunction] +md5::64:16 +sha1::64:20 + +#[PKCS] +#rsa::1024 +#dss::1024 + +[ServerInfo] +leevi.kuo.fi.ssh.com:10.2.1.7:Kuopio, Finland:1334 + +[AdminInfo] +Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi + +[ListenPort] +10.2.1.7:10.2.1.7:1334 + +[Logging] +infologfile:silcd.log:10000 +#warninglogfile:/var/log/silcd_warning.log:10000 +errorlogfile:leevi2_error.log:10000 +#fatallogfile:/var/log/silcd_error.log: + +[ConnectionClass] +1:100:100:100 +2:200:300:400 + +[ClientConnection] +10.2.1.199:passwd:priikone:333:1 +:::1333:1 + +[AdminConnection] +10.2.1.199:passwd:priikone:priikone:1 + +[ServerConnection] + +[RouterConnection] +10.2.1.7:passwd:priikone:1333:1:1 + +[DenyConnection] +[RedirectClient] diff --git a/apps/silcd/protocol.c b/apps/silcd/protocol.c new file mode 100644 index 00000000..79a487c8 --- /dev/null +++ b/apps/silcd/protocol.c @@ -0,0 +1,902 @@ +/* + + protocol.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * Server side of the protocols. + */ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "server_internal.h" + +SILC_TASK_CALLBACK(silc_server_protocol_connection_auth); +SILC_TASK_CALLBACK(silc_server_protocol_channel_auth); +SILC_TASK_CALLBACK(silc_server_protocol_key_exchange); + +/* SILC client protocol list */ +const SilcProtocolObject silc_protocol_list[] = +{ + { SILC_PROTOCOL_SERVER_CONNECTION_AUTH, + silc_server_protocol_connection_auth }, + { SILC_PROTOCOL_SERVER_CHANNEL_AUTH, + silc_server_protocol_channel_auth }, + { SILC_PROTOCOL_SERVER_KEY_EXCHANGE, + silc_server_protocol_key_exchange }, + + { SILC_PROTOCOL_SERVER_NONE, NULL }, +}; + +/* + * Key Exhange protocol functions + */ + +/* Packet sending callback. This function is provided as packet sending + routine to the Key Exchange functions. */ + +static void silc_server_protocol_ke_send_packet(SilcSKE ske, + SilcBuffer packet, + SilcPacketType type, + void *context) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerKEInternalContext *ctx = + (SilcServerKEInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + + /* Send the packet immediately */ + silc_server_packet_send(server, ske->sock, + type, 0, packet->data, packet->len, TRUE); +} + +/* Sets the negotiated key material into use for particular connection. */ + +static void silc_server_protocol_ke_set_keys(SilcSKE ske, + SilcSocketConnection sock, + SilcSKEKeyMaterial *keymat, + SilcCipher cipher, + SilcPKCS pkcs, + SilcHash hash, + int is_responder) +{ + SilcIDListUnknown *conn_data; + SilcHash nhash; + + SILC_LOG_DEBUG(("Setting new key into use")); + + conn_data = silc_calloc(1, sizeof(*conn_data)); + + /* Allocate cipher to be used in the communication */ + silc_cipher_alloc(cipher->cipher->name, &conn_data->send_key); + silc_cipher_alloc(cipher->cipher->name, &conn_data->receive_key); + + if (is_responder == TRUE) { + conn_data->send_key->cipher->set_key(conn_data->send_key->context, + keymat->receive_enc_key, + keymat->enc_key_len); + conn_data->send_key->set_iv(conn_data->send_key, keymat->receive_iv); + conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, + keymat->send_enc_key, + keymat->enc_key_len); + conn_data->receive_key->set_iv(conn_data->receive_key, keymat->send_iv); + + } else { + conn_data->send_key->cipher->set_key(conn_data->send_key->context, + keymat->send_enc_key, + keymat->enc_key_len); + conn_data->send_key->set_iv(conn_data->send_key, keymat->send_iv); + conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, + keymat->receive_enc_key, + keymat->enc_key_len); + conn_data->receive_key->set_iv(conn_data->receive_key, keymat->receive_iv); + } + + /* Allocate PKCS to be used */ +#if 0 + /* XXX Do we ever need to allocate PKCS for the connection?? + If yes, we need to change KE protocol to get the initiators + public key. */ + silc_pkcs_alloc(pkcs->pkcs->name, &conn_data->pkcs); + silc_pkcs_set_public_key(conn_data->pkcs, ske->ke2_payload->pk_data, + ske->ke2_payload->pk_len); +#endif + + /* Save HMAC key to be used in the communication. */ + silc_hash_alloc(hash->hash->name, &nhash); + silc_hmac_alloc(nhash, &conn_data->hmac); + conn_data->hmac_key_len = keymat->hmac_key_len; + conn_data->hmac_key = silc_calloc(conn_data->hmac_key_len, + sizeof(unsigned char)); + memcpy(conn_data->hmac_key, keymat->hmac_key, keymat->hmac_key_len); + + sock->user_data = (void *)conn_data; +} + +/* Performs key exchange protocol. This is used for both initiator + and responder key exchange. This is performed always when accepting + new connection to the server. This may be called recursively. */ + +SILC_TASK_CALLBACK(silc_server_protocol_key_exchange) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerKEInternalContext *ctx = + (SilcServerKEInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + SilcSKEStatus status = 0; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) + protocol->state = SILC_PROTOCOL_STATE_START; + + SILC_LOG_DEBUG(("State=%d", protocol->state)); + + switch(protocol->state) { + case SILC_PROTOCOL_STATE_START: + { + /* + * Start protocol + */ + SilcSKE ske; + + /* Allocate Key Exchange object */ + ske = silc_ske_alloc(); + ctx->ske = ske; + + if (ctx->responder == TRUE) { + /* Start the key exchange by processing the received security + properties packet from initiator. */ + status = silc_ske_responder_start(ske, ctx->rng, ctx->sock, + ctx->packet, NULL, NULL); + } else { + SilcSKEStartPayload *start_payload; + + /* Assemble security properties. */ + silc_ske_assemble_security_properties(ske, &start_payload); + + /* Start the key exchange by sending our security properties + to the remote end. */ + status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock, + start_payload, + silc_server_protocol_ke_send_packet, + context); + } + + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", + status)); + SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", + status)); + + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + return; + } + + /* Advance protocol state and call the next state if we are responder */ + protocol->state++; + if (ctx->responder == TRUE) + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000); + } + break; + case 2: + { + /* + * Phase 1 + */ + if (ctx->responder == TRUE) { + /* Sends the selected security properties to the initiator. */ + status = + silc_ske_responder_phase_1(ctx->ske, + ctx->ske->start_payload, + silc_server_protocol_ke_send_packet, + context); + } else { + /* 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 + save it. */ + status = + silc_ske_initiator_phase_1(ctx->ske, + ctx->packet, + NULL, NULL); + } + + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", + status)); + SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", + status)); + + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + return; + } + + /* Advance protocol state and call next state if we are initiator */ + protocol->state++; + if (ctx->responder == FALSE) + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000); + } + break; + case 3: + { + /* + * Phase 2 + */ + if (ctx->responder == TRUE) { + /* Process the received Key Exchange 1 Payload packet from + the initiator. This also creates our parts of the Diffie + Hellman algorithm. */ + status = + silc_ske_responder_phase_2(ctx->ske, ctx->packet, NULL, NULL); + } else { + /* Call the Phase-2 function. This creates Diffie Hellman + key exchange parameters and sends our public part inside + Key Exhange 1 Payload to the responder. */ + status = + silc_ske_initiator_phase_2(ctx->ske, + silc_server_protocol_ke_send_packet, + context); + } + + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", + status)); + SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", + status)); + + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + return; + } + + /* Advance protocol state and call the next state if we are responder */ + protocol->state++; + if (ctx->responder == TRUE) + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000); + } + break; + case 4: + { + /* + * Finish protocol + */ + if (ctx->responder == TRUE) { + unsigned char *pk, *prv; + unsigned int pk_len, prv_len; + + /* Get our public key to be sent to the initiator */ + pk = silc_pkcs_get_public_key(server->public_key, &pk_len); + + /* Get out private key to sign some data. */ + prv = silc_pkcs_get_private_key(server->public_key, &prv_len); + + /* This creates the key exchange material and sends our + public parts to the initiator inside Key Exchange 2 Payload. */ + status = + silc_ske_responder_finish(ctx->ske, + pk, pk_len, prv, prv_len, + SILC_SKE_PK_TYPE_SILC, + silc_server_protocol_ke_send_packet, + context); + + memset(pk, 0, pk_len); + memset(prv, 0, prv_len); + silc_free(pk); + silc_free(prv); + } else { + /* Finish the protocol. This verifies the Key Exchange 2 payload + sent by responder. */ + status = + silc_ske_initiator_finish(ctx->ske, + ctx->packet, NULL, NULL); + } + + if (status != SILC_SKE_STATUS_OK) { + SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol", + status)); + SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol", + status)); + + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + return; + } + + /* Send Ok to the other end. We will end the protocol as responder + sends Ok to us when we will take the new keys into use. */ + if (ctx->responder == FALSE) + silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context); + + /* End the protocol on the next round */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + break; + case SILC_PROTOCOL_STATE_END: + { + /* + * End protocol + */ + SilcSKEKeyMaterial *keymat; + + /* Send Ok to the other end if we are responder. If we are + initiator we have sent this already. */ + if (ctx->responder == TRUE) + silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context); + + /* Process the key material */ + keymat = silc_calloc(1, sizeof(*keymat)); + silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat); + + /* Take the new keys into use. */ + silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat, + ctx->ske->prop->cipher, + ctx->ske->prop->pkcs, + ctx->ske->prop->hash, + ctx->responder); + + /* Unregister the timeout task since the protocol has ended. + This was the timeout task to be executed if the protocol is + not completed fast enough. */ + if (ctx->timeout_task) + silc_task_unregister(server->timeout_queue, ctx->timeout_task); + + /* Call the final callback */ + if (protocol->final_callback) + protocol->execute_final(server->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + } + break; + case SILC_PROTOCOL_STATE_ERROR: + /* + * Error occured + */ + + /* Unregister the timeout task since the protocol has ended. + This was the timeout task to be executed if the protocol is + not completed fast enough. */ + if (ctx->timeout_task) + silc_task_unregister(server->timeout_queue, ctx->timeout_task); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + protocol->execute_final(server->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + break; + case SILC_PROTOCOL_STATE_UNKNOWN: + break; + } +} + +/* + * Connection Authentication protocol functions + */ + +/* Performs connection authentication protocol. If responder, we + authenticate the remote data received. If initiator, we will send + authentication data to the remote end. */ + +SILC_TASK_CALLBACK(silc_server_protocol_connection_auth) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerConnAuthInternalContext *ctx = + (SilcServerConnAuthInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN) + protocol->state = SILC_PROTOCOL_STATE_START; + + SILC_LOG_DEBUG(("State=%d", protocol->state)); + + switch(protocol->state) { + case SILC_PROTOCOL_STATE_START: + { + /* + * Start protocol. + */ + + if (ctx->responder == TRUE) { + /* + * We are receiving party + */ + unsigned short payload_len; + unsigned short conn_type; + unsigned char *auth_data; + + /* Parse the received authentication data packet. The received + payload is Connection Auth Payload. */ + silc_buffer_unformat(ctx->packet, + SILC_STR_UI_SHORT(&payload_len), + SILC_STR_UI_SHORT(&conn_type), + SILC_STR_END); + + if (payload_len != ctx->packet->len) { + SILC_LOG_ERROR(("Bad payload in authentication packet")); + SILC_LOG_DEBUG(("Bad payload in authentication packet")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + return; + } + + payload_len -= 4; + + if (conn_type < SILC_SOCKET_TYPE_CLIENT || + conn_type > SILC_SOCKET_TYPE_ROUTER) { + SILC_LOG_ERROR(("Bad connection type %d", conn_type)); + SILC_LOG_DEBUG(("Bad connection type %d", conn_type)); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000); + return; + } + + if (payload_len > 0) { + /* Get authentication data */ + silc_buffer_pull(ctx->packet, 4); + silc_buffer_unformat(ctx->packet, + SILC_STR_UI_XNSTRING_ALLOC(&auth_data, + payload_len), + SILC_STR_END); + } else { + auth_data = NULL; + } + + /* + * Check the remote connection type and make sure that we have + * configured this connection. If we haven't allowed this connection + * the authentication must be failed. + */ + + SILC_LOG_DEBUG(("Remote connection type %d", conn_type)); + + /* Remote end is client */ + if (conn_type == SILC_SOCKET_TYPE_CLIENT) { + SilcConfigServerSectionClientConnection *client = NULL; + client = + silc_config_server_find_client_conn(server->config, + ctx->sock->ip, + ctx->sock->port); + if (!client) + client = + silc_config_server_find_client_conn(server->config, + ctx->sock->hostname, + ctx->sock->port); + + if (client) { + switch(client->auth_meth) { + case SILC_PROTOCOL_CONN_AUTH_NONE: + /* No authentication required */ + SILC_LOG_DEBUG(("No authentication required")); + break; + + case SILC_PROTOCOL_CONN_AUTH_PASSWORD: + /* Password authentication */ + SILC_LOG_DEBUG(("Password authentication")); + if (auth_data) { + if (!memcmp(client->auth_data, auth_data, strlen(auth_data))) { + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + break; + } + } + + /* Authentication failed */ + SILC_LOG_ERROR(("Authentication failed")); + SILC_LOG_DEBUG(("Authentication failed")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + break; + + case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: + /* Public key authentication */ + SILC_LOG_DEBUG(("Public key authentication")); + if (auth_data) { + SilcIDListUnknown *conn_data; + SilcPKCS pkcs; + + conn_data = (SilcIDListUnknown *)ctx->sock->user_data; + + /* Load public key from file */ + if (silc_pkcs_load_public_key(client->auth_data, + &pkcs) == FALSE) { + + /* Authentication failed */ + SILC_LOG_ERROR(("Authentication failed " + "- could not read public key file")); + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + + /* Verify hash value HASH from KE protocol */ + if (pkcs->pkcs->verify(pkcs->context, + auth_data, payload_len, + ctx->ske->hash, + ctx->ske->hash_len) + == TRUE) { + silc_pkcs_free(pkcs); + break; + } + } + + SILC_LOG_ERROR(("Authentication failed")); + SILC_LOG_DEBUG(("Authentication failed")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + } else { + SILC_LOG_DEBUG(("No configuration for remote connection")); + SILC_LOG_ERROR(("Remote connection not configured")); + SILC_LOG_ERROR(("Authentication failed")); + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + } + + /* Remote end is server */ + if (conn_type == SILC_SOCKET_TYPE_SERVER) { + SilcConfigServerSectionServerConnection *serv = NULL; + serv = + silc_config_server_find_server_conn(server->config, + ctx->sock->ip, + ctx->sock->port); + if (!serv) + serv = + silc_config_server_find_server_conn(server->config, + ctx->sock->hostname, + ctx->sock->port); + + if (serv) { + switch(serv->auth_meth) { + case SILC_PROTOCOL_CONN_AUTH_NONE: + /* No authentication required */ + SILC_LOG_DEBUG(("No authentication required")); + break; + + case SILC_PROTOCOL_CONN_AUTH_PASSWORD: + /* Password authentication */ + SILC_LOG_DEBUG(("Password authentication")); + if (auth_data) { + if (!memcmp(serv->auth_data, auth_data, strlen(auth_data))) { + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + break; + } + } + + /* Authentication failed */ + SILC_LOG_ERROR(("Authentication failed")); + SILC_LOG_DEBUG(("Authentication failed")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + break; + + case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: + /* Public key authentication */ + SILC_LOG_DEBUG(("Public key authentication")); + if (auth_data) { + SilcIDListUnknown *conn_data; + SilcPKCS pkcs; + + conn_data = (SilcIDListUnknown *)ctx->sock->user_data; + + /* Load public key from file */ + if (silc_pkcs_load_public_key(serv->auth_data, + &pkcs) == FALSE) { + + /* Authentication failed */ + SILC_LOG_ERROR(("Authentication failed " + "- could not read public key file")); + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + + /* Verify hash value HASH from KE protocol */ + if (pkcs->pkcs->verify(pkcs->context, + auth_data, payload_len, + ctx->ske->hash, + ctx->ske->hash_len) + == TRUE) { + silc_pkcs_free(pkcs); + break; + } + } + + SILC_LOG_ERROR(("Authentication failed")); + SILC_LOG_DEBUG(("Authentication failed")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + } else { + SILC_LOG_DEBUG(("No configuration for remote connection")); + SILC_LOG_ERROR(("Remote connection not configured")); + SILC_LOG_ERROR(("Authentication failed")); + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + } + + /* Remote end is router */ + if (conn_type == SILC_SOCKET_TYPE_ROUTER) { + SilcConfigServerSectionServerConnection *serv = NULL; + serv = + silc_config_server_find_router_conn(server->config, + ctx->sock->ip, + ctx->sock->port); + if (!serv) + serv = + silc_config_server_find_router_conn(server->config, + ctx->sock->hostname, + ctx->sock->port); + + if (serv) { + switch(serv->auth_meth) { + case SILC_PROTOCOL_CONN_AUTH_NONE: + /* No authentication required */ + SILC_LOG_DEBUG(("No authentication required")); + break; + + case SILC_PROTOCOL_CONN_AUTH_PASSWORD: + /* Password authentication */ + SILC_LOG_DEBUG(("Password authentication")); + if (auth_data) { + if (!memcmp(serv->auth_data, auth_data, strlen(auth_data))) { + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + break; + } + } + + /* Authentication failed */ + SILC_LOG_ERROR(("Authentication failed")); + SILC_LOG_DEBUG(("Authentication failed")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + break; + + case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: + /* Public key authentication */ + SILC_LOG_DEBUG(("Public key authentication")); + if (auth_data) { + SilcIDListUnknown *conn_data; + SilcPKCS pkcs; + + conn_data = (SilcIDListUnknown *)ctx->sock->user_data; + + /* Load public key from file */ + if (silc_pkcs_load_public_key(serv->auth_data, + &pkcs) == FALSE) { + + /* Authentication failed */ + SILC_LOG_ERROR(("Authentication failed " + "- could not read public key file")); + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + + /* Verify hash value HASH from KE protocol */ + if (pkcs->pkcs->verify(pkcs->context, + auth_data, payload_len, + ctx->ske->hash, + ctx->ske->hash_len) + == TRUE) { + silc_pkcs_free(pkcs); + break; + } + } + + SILC_LOG_ERROR(("Authentication failed")); + SILC_LOG_DEBUG(("Authentication failed")); + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + } else { + SILC_LOG_DEBUG(("No configuration for remote connection")); + SILC_LOG_ERROR(("Remote connection not configured")); + SILC_LOG_ERROR(("Authentication failed")); + memset(auth_data, 0, payload_len); + silc_free(auth_data); + auth_data = NULL; + protocol->state = SILC_PROTOCOL_STATE_ERROR; + protocol->execute(server->timeout_queue, 0, + protocol, fd, 0, 300000); + return; + } + } + + if (auth_data) { + memset(auth_data, 0, payload_len); + silc_free(auth_data); + } + + /* Save connection type. This is later used to create the + ID for the connection. */ + ctx->conn_type = conn_type; + + /* Advance protocol state. */ + protocol->state = SILC_PROTOCOL_STATE_END; + protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0); + + } else { + /* + * We are initiator. We are authenticating ourselves to a + * remote server. We will send the authentication data to the + * other end for verify. + */ + SilcBuffer packet; + int payload_len = 0; + unsigned char *auth_data = NULL; + unsigned int auth_data_len = 0; + + switch(ctx->auth_meth) { + case SILC_PROTOCOL_CONN_AUTH_NONE: + /* No authentication required */ + break; + + case SILC_PROTOCOL_CONN_AUTH_PASSWORD: + /* Password authentication */ + if (ctx->auth_data && ctx->auth_data_len) { + auth_data = ctx->auth_data; + auth_data_len = ctx->auth_data_len; + break; + } + + /* No authentication data exits. Ask interactively from user. */ + /* XXX */ + + break; + + case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY: + /* Public key authentication */ + /* XXX TODO */ + break; + } + + payload_len = 4 + auth_data_len; + packet = silc_buffer_alloc(payload_len); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(payload_len), + SILC_STR_UI_SHORT(server->server_type + == SILC_SERVER ? + SILC_SOCKET_TYPE_SERVER : + SILC_SOCKET_TYPE_ROUTER), + SILC_STR_UI_XNSTRING(auth_data, auth_data_len), + SILC_STR_END); + + /* Send the packet to server */ + silc_server_packet_send(server, ctx->sock, + SILC_PACKET_CONNECTION_AUTH, 0, + packet->data, packet->len, TRUE); + + if (auth_data) { + memset(auth_data, 0, auth_data_len); + silc_free(auth_data); + } + silc_buffer_free(packet); + + /* Next state is end of protocol */ + protocol->state = SILC_PROTOCOL_STATE_END; + } + } + break; + + case SILC_PROTOCOL_STATE_END: + { + /* + * End protocol + */ + + /* Succesfully authenticated */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS, + 0, NULL, 0, TRUE); + + /* Unregister the timeout task since the protocol has ended. + This was the timeout task to be executed if the protocol is + not completed fast enough. */ + if (ctx->timeout_task) + silc_task_unregister(server->timeout_queue, ctx->timeout_task); + + /* Protocol has ended, call the final callback */ + if (protocol->final_callback) + protocol->execute_final(server->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + } + break; + case SILC_PROTOCOL_STATE_ERROR: + { + /* + * Error + */ + + /* Authentication failed */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_FAILURE, + 0, NULL, 0, TRUE); + + /* Unregister the timeout task since the protocol has ended. + This was the timeout task to be executed if the protocol is + not completed fast enough. */ + if (ctx->timeout_task) + silc_task_unregister(server->timeout_queue, ctx->timeout_task); + + /* On error the final callback is always called. */ + if (protocol->final_callback) + protocol->execute_final(server->timeout_queue, 0, protocol, fd); + else + silc_protocol_free(protocol); + } + break; + case SILC_PROTOCOL_STATE_UNKNOWN: + break; + } +} + +SILC_TASK_CALLBACK(silc_server_protocol_channel_auth) +{ +} diff --git a/apps/silcd/protocol.h b/apps/silcd/protocol.h new file mode 100644 index 00000000..b9505cf4 --- /dev/null +++ b/apps/silcd/protocol.h @@ -0,0 +1,83 @@ +/* + + protocol.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef PROTOCOL_H +#define PROTOCOL_H + +/* SILC client protocol types */ +#define SILC_PROTOCOL_SERVER_NONE 0 +#define SILC_PROTOCOL_SERVER_CONNECTION_AUTH 1 +#define SILC_PROTOCOL_SERVER_CHANNEL_AUTH 2 +#define SILC_PROTOCOL_SERVER_KEY_EXCHANGE 3 +/* #define SILC_PROTOCOL_SERVER_MAX 255 */ + +/* Internal context for Key Exchange protocol. */ +typedef struct { + void *server; + SilcSocketConnection sock; + SilcRng rng; + + /* TRUE if we are receiveing part of the protocol */ + int responder; + + /* Destinations ID taken from authenticataed packet so that we can + get the destinations ID. */ + void *dest_id; + SilcIdType dest_id_type; + + SilcTask timeout_task; + SilcBuffer packet; + SilcSKE ske; +} SilcServerKEInternalContext; + +/* Internal context for connection authentication protocol */ +typedef struct { + void *server; + SilcSocketConnection sock; + + /* TRUE if we are receiving part of the protocol */ + int responder; + + /* SKE object from Key Exchange protocol. */ + SilcSKE ske; + + /* Auth method that must be used. This is resolved before this + connection authentication protocol is started. Used when we are + initiating. */ + unsigned int auth_meth; + + /* Authentication data if we alreay know it. This is filled before + starting the protocol if we know the authentication data. Otherwise + these are and remain NULL. Used when we are initiating. */ + unsigned char *auth_data; + unsigned int auth_data_len; + + /* Destinations ID from KE protocol context */ + void *dest_id; + SilcIdType dest_id_type; + + SilcTask timeout_task; + SilcBuffer packet; + unsigned short conn_type; +} SilcServerConnAuthInternalContext; + +/* Prototypes */ + +#endif diff --git a/apps/silcd/pubkey.pub b/apps/silcd/pubkey.pub new file mode 100644 index 0000000000000000000000000000000000000000..cf4ed9bf318650c16067480b026964492386d3bb GIT binary patch literal 330 zcmZvYF>Zq~2!)dl9de3XAd)f0z?w9wel?0JRUJA4#s^66FV*c_*7x2AywhpT`TDxu zk00CZa(w?h-?q!~?fUgAoDPp~+duD~cKor+e79LoruU{s$`K~&H!r;Rk} zGtk_Dk^17NuT@>yvJlo<3PNqmQnKLma9E%sfs=fib)KWIBS=`fT3XO#_R%E=uB;JJ VXeGoR0Jy%U!(SuU`^*1L{QyBbRmK1S literal 0 HcmV?d00001 diff --git a/apps/silcd/route.c b/apps/silcd/route.c new file mode 100644 index 00000000..dad5ae3b --- /dev/null +++ b/apps/silcd/route.c @@ -0,0 +1,65 @@ +/* + + route.c + + Author: Pekka Riikonen + + Copyright (C) 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * Route cache routines. Server uses these to route packets to specific + * routes. If route entry doesn't exist for a specific destination, server + * uses primary route (default route). + */ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "route.h" + +/* Route cache hash table */ +SilcServerRouteTable silc_route_cache[SILC_SERVER_ROUTE_SIZE]; + +/* Adds new route to the route cache. The argument `index' is the + index value generated by silc_server_route_hash. */ + +void silc_server_route_add(unsigned int index, unsigned int dest, + SilcServerList *router) +{ + silc_route_cache[index].dest = dest; + silc_route_cache[index].router = router; +} + +/* Checksk whether destination has a specific router. Returns the + router data if found, NULL otherwise. */ + +SilcServerList *silc_server_route_check(unsigned int dest, + unsigned short port) +{ + unsigned int index; + + index = silc_server_route_hash(dest, port); + + if (silc_route_cache[index].router != NULL && + silc_route_cache[index].dest == dest) + return silc_route_cache[index].router; + + return NULL; +} diff --git a/apps/silcd/route.h b/apps/silcd/route.h new file mode 100644 index 00000000..aec76e69 --- /dev/null +++ b/apps/silcd/route.h @@ -0,0 +1,77 @@ +/* + + route.h + + Author: Pekka Riikonen + + Copyright (C) 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef ROUTE_H +#define ROUTE_H + +/* Definitions */ + +/* Size of the route cache hash table */ +#define SILC_SERVER_ROUTE_SIZE 256 + +/* + SILC Server Route table + + Following short description of the fields. + + unsigned int dest + + Destination IPv4 address. Can be used to quickly check whether + the found route entry is what the caller wanted. + + SilcServerList *router + + Pointer to the router specific data. + +*/ +typedef struct { + unsigned int dest; + SilcServerList *router; +} SilcServerRouteTable; + +/* Route cache hash table */ +extern SilcServerRouteTable silc_route_cache[SILC_SERVER_ROUTE_SIZE]; + +/* Macros and super macros */ + +/* Returns route cache hash table entry index. This is IPv4 specific. + `port' argument may be zero (0) if it doesn't exist. This has been + taken from Linux kernel's route cache code. */ +extern inline +unsigned int silc_server_route_hash(unsigned int addr, + unsigned short port) +{ + unsigned int hash; + + hash = ((addr & 0xf0f0f0f0) >> 4) | ((addr & 0x0f0f0f0f) << 4); + hash ^= port; + hash ^= (hash >> 16); + hash ^= (hash >> 8); + + return hash & 0xff; +} + +/* Prototypes */ +void silc_server_route_add(unsigned int index, unsigned int dest, + SilcServerList *router); +SilcServerList *silc_server_route_check(unsigned int dest, + unsigned short port); + +#endif diff --git a/apps/silcd/server.c b/apps/silcd/server.c new file mode 100644 index 00000000..4da53bdf --- /dev/null +++ b/apps/silcd/server.c @@ -0,0 +1,3726 @@ +/* + + server.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * This is the actual SILC server than handles everything relating to + * servicing the SILC connections. This is also a SILC router as a router + * is also normal server. + */ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "server_internal.h" + +/* Static prototypes */ +SILC_TASK_CALLBACK(silc_server_connect_to_router); +SILC_TASK_CALLBACK(silc_server_connect_to_router_second); +SILC_TASK_CALLBACK(silc_server_connect_to_router_final); +SILC_TASK_CALLBACK(silc_server_accept_new_connection); +SILC_TASK_CALLBACK(silc_server_accept_new_connection_second); +SILC_TASK_CALLBACK(silc_server_accept_new_connection_final); +SILC_TASK_CALLBACK(silc_server_packet_process); +SILC_TASK_CALLBACK(silc_server_packet_parse); +SILC_TASK_CALLBACK(silc_server_timeout_remote); + +/* XXX */ +void silc_server_packet_parse_type(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); + +static int silc_server_packet_check_mac(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer); +static int silc_server_packet_decrypt_rest(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer); +static int silc_server_packet_decrypt_rest_special(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer); + +extern char server_version[]; + +/* Allocates a new SILC server object. This has to be done before the server + can be used. After allocation one must call silc_server_init to initialize + the server. The new allocated server object is returned to the new_server + argument. */ + +int silc_server_alloc(SilcServer *new_server) +{ + SILC_LOG_DEBUG(("Allocating new server object")); + + *new_server = silc_calloc(1, sizeof(**new_server)); + if (*new_server == NULL) { + SILC_LOG_ERROR(("Could not allocate new server object")); + return FALSE; + } + + /* Set default values */ + (*new_server)->server_name = NULL; + (*new_server)->server_type = SILC_SERVER; + (*new_server)->standalone = FALSE; + (*new_server)->id = NULL; + (*new_server)->io_queue = NULL; + (*new_server)->timeout_queue = NULL; + (*new_server)->local_list = silc_calloc(1, sizeof(SilcIDListObject)); + (*new_server)->global_list = silc_calloc(1, sizeof(SilcIDListObject)); + (*new_server)->rng = NULL; + (*new_server)->md5hash = NULL; + (*new_server)->sha1hash = NULL; + /* (*new_server)->public_key = NULL;*/ + + return TRUE; +} + +/* Free's the SILC server object. This is called at the very end before + the program ends. */ + +void silc_server_free(SilcServer server) +{ + if (server) { + if (server->local_list) + silc_free(server->local_list); + if (server->global_list) + silc_free(server->global_list); + if (server->rng) + silc_rng_free(server->rng); + + silc_math_primegen_uninit(); /* XXX */ + silc_free(server); + } +} + +/* Initializes the entire SILC server. This is called always before running + the server. This is called only once at the initialization of the program. + This binds the server to its listenning port. After this function returns + one should call silc_server_run to start the server. This returns TRUE + when everything is ok to run the server. Configuration file must be + read and parsed before calling this. */ + +int silc_server_init(SilcServer server) +{ + int *sock = NULL, sock_count, i; + SilcServerID *id; + SilcServerList *id_entry; + SilcHashObject hash; + + SILC_LOG_DEBUG(("Initializing server")); + assert(server); + assert(server->config); + + /* Set log files where log message should be saved. */ + server->config->server = server; + silc_config_server_setlogfiles(server->config); + + /* Register all configured ciphers, PKCS and hash functions. */ + silc_config_server_register_ciphers(server->config); + silc_config_server_register_pkcs(server->config); + silc_config_server_register_hashfuncs(server->config); + + /* Initialize random number generator for the server. */ + server->rng = silc_rng_alloc(); + silc_rng_init(server->rng); + silc_math_primegen_init(); /* XXX */ + + /* Initialize hash functions for server to use */ + silc_hash_alloc("md5", &server->md5hash); + silc_hash_alloc("sha1", &server->sha1hash); + + /* Initialize none cipher */ + silc_cipher_alloc("none", &server->none_cipher); + + /* XXXXX Generate RSA key pair */ + { + unsigned char *public_key; + unsigned char *private_key; + unsigned int pk_len, prv_len; + + if (silc_pkcs_alloc("rsa", &server->public_key) == FALSE) { + SILC_LOG_ERROR(("Could not create RSA key pair")); + goto err0; + } + + if (server->public_key->pkcs->init(server->public_key->context, + 1024, server->rng) == FALSE) { + SILC_LOG_ERROR(("Could not generate RSA key pair")); + goto err0; + } + + public_key = + server->public_key->pkcs->get_public_key(server->public_key->context, + &pk_len); + private_key = + server->public_key->pkcs->get_private_key(server->public_key->context, + &prv_len); + + SILC_LOG_HEXDUMP(("public key"), public_key, pk_len); + SILC_LOG_HEXDUMP(("private key"), private_key, prv_len); + + /* XXX Save keys */ + silc_pkcs_save_public_key(server->public_key, "pubkey.pub", + public_key, pk_len); + + memset(public_key, 0, pk_len); + memset(private_key, 0, prv_len); + silc_free(public_key); + silc_free(private_key); + } + + /* Create a listening server. Note that our server can listen on + multiple ports. All listeners are created here and now. */ + /* XXX Still check this whether to use server_info or listen_port. */ + sock_count = 0; + while(server->config->listen_port) { + int tmp; + + tmp = silc_net_create_server(server->config->listen_port->port, + server->config->listen_port->host); + if (tmp < 0) + goto err0; + + sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1))); + sock[sock_count] = tmp; + server->config->listen_port = server->config->listen_port->next; + sock_count++; + } + + /* Allocate the entire socket list that is used in server. Eventually + all connections will have entry in this table (it is a table of + pointers to the actual object that is allocated individually + later). */ + server->sockets = silc_calloc(SILC_SERVER_MAX_CONNECTIONS, + sizeof(*server->sockets)); + + for (i = 0; i < sock_count; i++) { + SilcSocketConnection newsocket = NULL; + + /* Set socket to non-blocking mode */ + silc_net_set_socket_nonblock(sock[i]); + server->sock = sock[i]; + + /* Create a Server ID for the server. */ + silc_id_create_server_id(sock[i], server->rng, &id); + if (!id) { + goto err0; + } + + server->id = id; + server->id_type = SILC_ID_SERVER; + server->server_name = server->config->server_info->server_name; + + /* Add ourselves to the server list. We don't have a router yet + beacuse we haven't established a route yet. It will be done later. + For now, NULL is sent as router. This allocates new entry to + the ID list. */ + silc_idlist_add_server(&server->local_list->servers, + server->config->server_info->server_name, + server->server_type, server->id, NULL, + server->send_key, server->receive_key, + NULL, NULL, &id_entry); + if (!id_entry) + goto err0; + + /* Add ourselves also to the socket table. The entry allocated above + is sent as argument for fast referencing in the future. */ + silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, + &newsocket); + if (!newsocket) + goto err0; + + server->sockets[sock[i]] = newsocket; + + /* Put the allocated socket pointer also to the entry allocated above + for fast back-referencing to the socket list. */ + id_entry->connection = (void *)server->sockets[sock[i]]; + server->id_entry = id_entry; + } + + /* Register the task queues. In SILC we have by default three task queues. + One task queue for non-timeout tasks which perform different kind of + I/O on file descriptors, timeout task queue for timeout tasks, and, + generic non-timeout task queue whose tasks apply to all connections. */ + silc_task_queue_alloc(&server->io_queue, TRUE); + if (!server->io_queue) { + goto err0; + } + silc_task_queue_alloc(&server->timeout_queue, TRUE); + if (!server->timeout_queue) { + goto err1; + } + silc_task_queue_alloc(&server->generic_queue, TRUE); + if (!server->generic_queue) { + goto err1; + } + + /* Initialize the scheduler */ + silc_schedule_init(server->io_queue, server->timeout_queue, + server->generic_queue, + SILC_SERVER_MAX_CONNECTIONS); + + /* Add the first task to the queue. This is task that is executed by + timeout. It expires as soon as the caller calls silc_server_run. This + task performs authentication protocol and key exchange with our + primary router. */ + if (silc_task_register(server->timeout_queue, sock[0], + silc_server_connect_to_router, + (void *)server, 0, 1, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL) == NULL) { + goto err2; + } + + /* If server connections has been configured then we must be router as + normal server cannot have server connections, only router connections. */ + if (server->config->servers) + server->server_type = SILC_ROUTER; + + SILC_LOG_DEBUG(("Server initialized")); + + /* We are done here, return succesfully */ + return TRUE; + + err2: + silc_task_queue_free(server->timeout_queue); + err1: + silc_task_queue_free(server->io_queue); + err0: + for (i = 0; i < sock_count; i++) + silc_net_close_server(sock[i]); + + return FALSE; +} + +/* Stops the SILC server. This function is used to shutdown the server. + This is usually called after the scheduler has returned. After stopping + the server one should call silc_server_free. */ + +void silc_server_stop(SilcServer server) +{ + SILC_LOG_DEBUG(("Stopping server")); + + /* Stop the scheduler, although it might be already stopped. This + doesn't hurt anyone. This removes all the tasks and task queues, + as well. */ + silc_schedule_stop(); + silc_schedule_uninit(); + + SILC_LOG_DEBUG(("Server stopped")); +} + +/* The heart of the server. This runs the scheduler thus runs the server. */ + +void silc_server_run(SilcServer server) +{ + SILC_LOG_DEBUG(("Running server")); + + /* Start the scheduler, the heart of the SILC server. When this returns + the program will be terminated. */ + silc_schedule(); +} + +/* This function connects to our primary router or if we are a router this + establishes all our primary routes. This is called at the start of the + server to do authentication and key exchange with our router - called + from schedule. */ + +SILC_TASK_CALLBACK(silc_server_connect_to_router) +{ + SilcServer server = (SilcServer)context; + SilcSocketConnection newsocket; + int sock; + + SILC_LOG_DEBUG(("Connecting to router(s)")); + + /* if we are normal SILC server we need to connect to our cell's + router. */ + if (server->server_type == SILC_SERVER) { + SilcProtocol protocol; + SilcServerKEInternalContext *proto_ctx; + + /* Create connection to the router, if configured. */ + if (server->config->routers) { + sock = silc_net_create_connection(server->config->routers->port, + server->config->routers->host); + if (sock < 0) { + SILC_LOG_ERROR(("Could not connect to router")); + silc_schedule_stop(); + return; + } + + /* Set socket options */ + silc_net_set_socket_nonblock(sock); + silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + + /* Create socket connection for the connection. Even though we + know that we are connecting to a router we will mark the socket + to be unknown connection until we have executed authentication + protocol. */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); + server->sockets[sock] = newsocket; + newsocket->hostname = server->config->routers->host; + newsocket->port = server->config->routers->port; + + /* Allocate internal protocol context. This is sent as context + to the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = context; + proto_ctx->sock = newsocket; + proto_ctx->rng = server->rng; + proto_ctx->responder = FALSE; + + /* Perform key exchange protocol. silc_server_connect_to_router_second + will be called after the protocol is finished. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, + &protocol, proto_ctx, + silc_server_connect_to_router_second); + newsocket->protocol = protocol; + + /* Register a timeout task that will be executed if the protocol + is not executed within 15 seconds. For now, this is a hard coded + limit. After 15 secs the connection will be closed if the key + exchange protocol has not been executed. */ + proto_ctx->timeout_task = + silc_task_register(server->timeout_queue, sock, + silc_server_timeout_remote, + context, 15, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as + well. However, this doesn't set the scheduler for outgoing traffic, + it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + SILC_REGISTER_CONNECTION_FOR_IO(sock); + + /* Run the protocol */ + protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0); + return; + } + } + + /* if we are a SILC router we need to establish all of our primary + routes. */ + if (server->server_type == SILC_ROUTER) { + SilcConfigServerSectionServerConnection *ptr; + + /* Create the connections to all our routes */ + ptr = server->config->routers; + while (ptr) { + SilcProtocol protocol; + SilcServerKEInternalContext *proto_ctx; + + /* Create the connection to the remote end */ + sock = silc_net_create_connection(ptr->port, ptr->host); + if (sock < 0) { + SILC_LOG_ERROR(("Could not connect to router")); + silc_schedule_stop(); + return; + } + + /* Set socket options */ + silc_net_set_socket_nonblock(sock); + silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + + /* Create socket connection for the connection. Even though we + know that we are connecting to a router we will mark the socket + to be unknown connection until we have executed authentication + protocol. */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); + server->sockets[sock] = newsocket; + newsocket->hostname = ptr->host; + newsocket->port = ptr->port; + + /* Allocate internal protocol context. This is sent as context + to the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = context; + proto_ctx->sock = newsocket; + proto_ctx->rng = server->rng; + proto_ctx->responder = FALSE; + + /* Perform key exchange protocol. silc_server_connect_to_router_final + will be called after the protocol is finished. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, + &protocol, proto_ctx, + silc_server_connect_to_router_second); + newsocket->protocol = protocol; + + /* Register a timeout task that will be executed if the protocol + is not executed within 15 seconds. For now, this is a hard coded + limit. After 15 secs the connection will be closed if the key + exchange protocol has not been executed. */ + proto_ctx->timeout_task = + silc_task_register(server->timeout_queue, sock, + silc_server_timeout_remote, + context, 15, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as + well. However, this doesn't set the scheduler for outgoing traffic, + it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + SILC_REGISTER_CONNECTION_FOR_IO(sock); + + /* Run the protocol */ + protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0); + + if (!ptr->next) + return; + + ptr = ptr->next; + } + } + + SILC_LOG_DEBUG(("No router(s), server will be standalone")); + + /* There wasn't a configured router, we will continue but we don't + have a connection to outside world. We will be standalone server. */ + server->standalone = TRUE; + + /* Add a task to the queue. This task receives new connections to the + server. This task remains on the queue until the end of the program. */ + if (silc_task_register(server->io_queue, fd, + silc_server_accept_new_connection, + (void *)server, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL) == NULL) { + silc_schedule_stop(); + return; + } +} + +/* Second part of connecting to router(s). Key exchange protocol has been + executed and now we will execute authentication protocol. */ + +SILC_TASK_CALLBACK(silc_server_connect_to_router_second) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerKEInternalContext *ctx = + (SilcServerKEInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + SilcSocketConnection sock = NULL; + SilcServerConnAuthInternalContext *proto_ctx; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { + /* Error occured during protocol */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Key exchange failed"); + return; + } + + /* Allocate internal context for the authentication protocol. This + is sent as context for the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = (void *)server; + proto_ctx->sock = sock = server->sockets[fd]; + proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */ + proto_ctx->dest_id_type = ctx->dest_id_type; + proto_ctx->dest_id = ctx->dest_id; + + /* Resolve the authentication method used in this connection */ + proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD; + if (server->config->routers) { + SilcConfigServerSectionServerConnection *conn = NULL; + + /* Check if we find a match from user configured connections */ + conn = silc_config_server_find_router_conn(server->config, + sock->hostname, + sock->port); + if (conn) { + /* Match found. Use the configured authentication method */ + proto_ctx->auth_meth = conn->auth_meth; + if (conn->auth_data) { + proto_ctx->auth_data = strdup(conn->auth_data); + proto_ctx->auth_data_len = strlen(conn->auth_data); + } + } else { + /* No match found. */ + /* XXX */ + } + } else { + /* XXX */ + } + + /* Free old protocol as it is finished now */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + silc_free(ctx); + sock->protocol = NULL; + + /* Allocate the authentication protocol. This is allocated here + but we won't start it yet. We will be receiving party of this + protocol thus we will wait that connecting party will make + their first move. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, + &sock->protocol, proto_ctx, + silc_server_connect_to_router_final); + + /* Register timeout task. If the protocol is not executed inside + this timelimit the connection will be terminated. Currently + this is 15 seconds and is hard coded limit (XXX). */ + proto_ctx->timeout_task = + silc_task_register(server->timeout_queue, sock->sock, + silc_server_timeout_remote, + (void *)server, 15, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + /* Run the protocol */ + sock->protocol->execute(server->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); +} + +/* Finalizes the connection to router. Registers a server task to the + queue so that we can accept new connections. */ + +SILC_TASK_CALLBACK(silc_server_connect_to_router_final) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerConnAuthInternalContext *ctx = + (SilcServerConnAuthInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + SilcSocketConnection sock = ctx->sock; + SilcServerList *id_entry; + SilcIDListUnknown *conn_data; + SilcBuffer packet; + unsigned char *id_string; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { + /* Error occured during protocol */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Authentication failed"); + return; + } + + /* Add a task to the queue. This task receives new connections to the + server. This task remains on the queue until the end of the program. */ + if (!server->listenning) { + if (silc_task_register(server->io_queue, server->sock, + silc_server_accept_new_connection, + (void *)server, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL) == NULL) { + silc_schedule_stop(); + return; + } else { + server->listenning = TRUE; + } + } + + /* Send NEW_SERVER packet to the router. We will become registered + to the SILC network after sending this packet. */ + id_string = silc_id_id2str(server->id, SILC_ID_SERVER); + packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN + + strlen(server->server_name)); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN), + SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN), + SILC_STR_UI_SHORT(strlen(server->server_name)), + SILC_STR_UI_XNSTRING(server->server_name, + strlen(server->server_name)), + SILC_STR_END); + + /* Send the packet */ + silc_server_packet_send(server, ctx->sock, SILC_PACKET_NEW_SERVER, 0, + packet->data, packet->len, TRUE); + silc_buffer_free(packet); + silc_free(id_string); + + SILC_LOG_DEBUG(("Connected to router %s", sock->hostname)); + + /* Add the connected router to local server list */ + server->standalone = FALSE; + conn_data = (SilcIDListUnknown *)sock->user_data; + silc_idlist_add_server(&server->local_list->servers, + sock->hostname ? sock->hostname : sock->ip, + SILC_ROUTER, ctx->dest_id, NULL, + conn_data->send_key, conn_data->receive_key, + conn_data->pkcs, conn_data->hmac, &id_entry); + + id_entry->hmac_key = conn_data->hmac_key; + id_entry->hmac_key_len = conn_data->hmac_key_len; + id_entry->connection = sock; + sock->user_data = (void *)id_entry; + sock->type = SILC_SOCKET_TYPE_ROUTER; + server->id_entry->router = id_entry; + + /* Free the temporary connection data context from key exchange */ + silc_free(conn_data); + + /* Free the protocol object */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + silc_free(ctx); + sock->protocol = NULL; +} + +/* Accepts new connections to the server. Accepting new connections are + done in three parts to make it async. */ + +SILC_TASK_CALLBACK(silc_server_accept_new_connection) +{ + SilcServer server = (SilcServer)context; + SilcSocketConnection newsocket; + SilcServerKEInternalContext *proto_ctx; + int sock; + + SILC_LOG_DEBUG(("Accepting new connection")); + + sock = silc_net_accept_connection(server->sock); + if (sock < 0) { + SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno))); + return; + } + + /* Check max connections */ + if (sock > SILC_SERVER_MAX_CONNECTIONS) { + if (server->config->redirect) { + /* XXX Redirecting connection to somewhere else now?? */ + /*silc_server_send_notify("Server is full, trying to redirect..."); */ + } else { + SILC_LOG_ERROR(("Refusing connection, server is full")); + } + return; + } + + /* Set socket options */ + silc_net_set_socket_nonblock(sock); + silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + + /* We don't create a ID yet, since we don't know what type of connection + this is yet. But, we do add the connection to the socket table. */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); + server->sockets[sock] = newsocket; + + /* XXX This MUST be done async as this will block the entire server + process. Either we have to do our own resolver stuff or in the future + we can use threads. */ + /* Perform mandatory name and address lookups for the remote host. */ + silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip); + if (!newsocket->ip || !newsocket->hostname) { + SILC_LOG_DEBUG(("IP lookup/DNS lookup failed")); + SILC_LOG_ERROR(("IP lookup/DNS lookup failed")); + return; + } + + /* Allocate internal context for key exchange protocol. This is + sent as context for the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = context; + proto_ctx->sock = newsocket; + proto_ctx->rng = server->rng; + proto_ctx->responder = TRUE; + + /* Prepare the connection for key exchange protocol. We allocate the + protocol but will not start it yet. The connector will be the + initiator of the protocol thus we will wait for initiation from + there before we start the protocol. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, + &newsocket->protocol, proto_ctx, + silc_server_accept_new_connection_second); + + /* Register a timeout task that will be executed if the connector + will not start the key exchange protocol within 15 seconds. For + now, this is a hard coded limit. After 15 secs the connection will + be closed if the key exchange protocol has not been started. */ + proto_ctx->timeout_task = + silc_task_register(server->timeout_queue, newsocket->sock, + silc_server_timeout_remote, + context, 15, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as well. + However, this doesn't set the scheduler for outgoing traffic, it + will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + SILC_REGISTER_CONNECTION_FOR_IO(sock); +} + +/* Second part of accepting new connection. Key exchange protocol has been + performed and now it is time to do little connection authentication + protocol to figure out whether this connection is client or server + and whether it has right to access this server (especially server + connections needs to be authenticated). */ + +SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerKEInternalContext *ctx = + (SilcServerKEInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + SilcSocketConnection sock = NULL; + SilcServerConnAuthInternalContext *proto_ctx; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { + /* Error occured during protocol */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Key exchange failed"); + return; + } + + /* Allocate internal context for the authentication protocol. This + is sent as context for the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = (void *)server; + proto_ctx->sock = sock = server->sockets[fd]; + proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */ + proto_ctx->responder = TRUE; + proto_ctx->dest_id_type = ctx->dest_id_type; + proto_ctx->dest_id = ctx->dest_id; + + /* Free old protocol as it is finished now */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + silc_free(ctx); + sock->protocol = NULL; + + /* Allocate the authentication protocol. This is allocated here + but we won't start it yet. We will be receiving party of this + protocol thus we will wait that connecting party will make + their first move. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, + &sock->protocol, proto_ctx, + silc_server_accept_new_connection_final); + + /* Register timeout task. If the protocol is not executed inside + this timelimit the connection will be terminated. Currently + this is 60 seconds and is hard coded limit (XXX). */ + proto_ctx->timeout_task = + silc_task_register(server->timeout_queue, sock->sock, + silc_server_timeout_remote, + (void *)server, 60, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); +} + +/* Final part of accepting new connection. The connection has now + been authenticated and keys has been exchanged. We also know whether + this is client or server connection. */ + +SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) +{ + SilcProtocol protocol = (SilcProtocol)context; + SilcServerConnAuthInternalContext *ctx = + (SilcServerConnAuthInternalContext *)protocol->context; + SilcServer server = (SilcServer)ctx->server; + SilcSocketConnection sock = ctx->sock; + + SILC_LOG_DEBUG(("Start")); + + if (protocol->state == SILC_PROTOCOL_STATE_ERROR) { + /* Error occured during protocol */ + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, "Server closed connection: " + "Authentication failed"); + return; + } + + sock->type = ctx->conn_type; + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + { + SilcClientList *id_entry = NULL; + SilcIDListUnknown *conn_data = sock->user_data; + + SILC_LOG_DEBUG(("Remote host is client")); + + /* Add the client to the client ID list. We have not created the + client ID for the client yet. This is done when client registers + itself by sending NEW_CLIENT packet. */ + silc_idlist_add_client(&server->local_list->clients, + NULL, NULL, NULL, NULL, NULL, + conn_data->send_key, conn_data->receive_key, + conn_data->pkcs, conn_data->hmac, &id_entry); + + id_entry->hmac_key = conn_data->hmac_key; + id_entry->hmac_key_len = conn_data->hmac_key_len; + id_entry->connection = sock; + + /* Free the temporary connection data context from key exchange */ + silc_free(conn_data); + + /* Mark the entry to the ID list to the socket connection for + fast referencing in the future. */ + sock->user_data = (void *)id_entry; + break; + } + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + { + SilcServerList *id_entry; + SilcIDListUnknown *conn_data = sock->user_data; + + SILC_LOG_DEBUG(("Remote host is %s", + sock->type == SILC_SOCKET_TYPE_SERVER ? + "server" : "router")); + + /* Add the server to the ID list. We don't have the server's ID + yet but we will receive it after the server sends NEW_SERVER + packet to us. */ + silc_idlist_add_server(&server->local_list->servers, NULL, + sock->type == SILC_SOCKET_TYPE_SERVER ? + SILC_SERVER : SILC_ROUTER, NULL, NULL, + conn_data->send_key, conn_data->receive_key, + conn_data->pkcs, conn_data->hmac, &id_entry); + + id_entry->hmac_key = conn_data->hmac_key; + id_entry->hmac_key_len = conn_data->hmac_key_len; + id_entry->connection = sock; + + /* Free the temporary connection data context from key exchange */ + silc_free(conn_data); + + /* Mark the entry to the ID list to the socket connection for + fast referencing in the future. */ + sock->user_data = (void *)id_entry; + + /* There is connection to other server now, if it is router then + we will have connection to outside world. If we are router but + normal server connected to us then we will remain standalone, + if we are standlone. */ + if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) { + SILC_LOG_DEBUG(("We are not standalone server anymore")); + server->standalone = FALSE; + } + break; + } + default: + break; + } + + /* Connection has been fully established now. Everything is ok. */ + SILC_LOG_DEBUG(("New connection authenticated")); + + silc_protocol_free(protocol); + if (ctx->packet) + silc_buffer_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + if (ctx->dest_id) + silc_free(ctx->dest_id); + silc_free(ctx); + sock->protocol = NULL; +} + +typedef struct { + SilcPacketContext *packetdata; + SilcServer server; + SilcSocketConnection sock; + SilcCipher cipher; + SilcHmac hmac; +} SilcServerInternalPacket; + +/* This function is used to read packets from network and send packets to + network. This is usually a generic task. */ + +SILC_TASK_CALLBACK(silc_server_packet_process) +{ + SilcServer server = (SilcServer)context; + SilcSocketConnection sock = server->sockets[fd]; + int ret, packetlen, paddedlen; + + SILC_LOG_DEBUG(("Processing packet")); + + /* Packet sending */ + if (type == SILC_TASK_WRITE) { + SILC_LOG_DEBUG(("Writing data to connection")); + + if (sock->outbuf->data - sock->outbuf->head) + silc_buffer_push(sock->outbuf, + sock->outbuf->data - sock->outbuf->head); + + /* Write the packet out to the connection */ + ret = silc_packet_write(fd, sock->outbuf); + + /* If returned -2 could not write to connection now, will do + it later. */ + if (ret == -2) + return; + + /* Error */ + if (ret == -1) + SILC_LOG_ERROR(("Could not write, packet dropped")); + + /* The packet has been sent and now it is time to set the connection + back to only for input. When there is again some outgoing data + available for this connection it will be set for output as well. + This call clears the output setting and sets it only for input. */ + SILC_SET_CONNECTION_FOR_INPUT(fd); + SILC_UNSET_OUTBUF_PENDING(sock); + + silc_buffer_clear(sock->outbuf); + return; + } + + /* Packet receiving */ + if (type == SILC_TASK_READ) { + SILC_LOG_DEBUG(("Reading data from connection")); + + /* Allocate the incoming data buffer if not done already. */ + if (!sock->inbuf) + sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE); + + /* Read some data from connection */ + ret = silc_packet_read(fd, sock->inbuf); + + /* If returned -2 data was not available now, will read it later. */ + if (ret == -2) + return; + + /* Error */ + if (ret == -1) { + SILC_LOG_ERROR(("Could not read, packet dropped")); + return; + } + + /* EOF */ + if (ret == 0) { + SILC_LOG_DEBUG(("Read EOF")); + + /* If connection is disconnecting already we will finally + close the connection */ + if (SILC_IS_DISCONNECTING(sock)) { + if (sock->user_data) + silc_server_free_sock_user_data(server, sock); + silc_server_close_connection(server, sock); + return; + } + + SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock)); + + if (sock->user_data) + silc_server_free_sock_user_data(server, sock); + silc_server_close_connection(server, sock); + return; + } + + /* If connection is disconnecting or disconnected we will ignore + what we read. */ + if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) { + SILC_LOG_DEBUG(("Ignoring read data from invalid connection")); + return; + } + + /* Check whether we received a whole packet. If reading went without + errors we either read a whole packet or the read packet is + incorrect and will be dropped. */ + SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen); + if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) { + SILC_LOG_DEBUG(("Received incorrect packet, dropped")); + silc_buffer_clear(sock->inbuf); + silc_server_disconnect_remote(server, sock, "Incorrect packet"); + return; + } + + /* Decrypt a packet coming from client. */ + if (sock->type == SILC_SOCKET_TYPE_CLIENT) { + SilcClientList *clnt = (SilcClientList *)sock->user_data; + SilcServerInternalPacket *packet; + int mac_len = 0; + + if (clnt->hmac) + mac_len = clnt->hmac->hash->hash->hash_len; + + if (sock->inbuf->len - 2 > (paddedlen + mac_len)) { + /* Received possibly many packets at once */ + + while(sock->inbuf->len > 0) { + SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen); + if (sock->inbuf->len < paddedlen) { + SILC_LOG_DEBUG(("Receive incorrect packet, dropped")); + return; + } + + paddedlen += 2; + packet = silc_calloc(1, sizeof(*packet)); + packet->server = server; + packet->sock = sock; + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len); + silc_buffer_pull_tail(packet->packetdata->buffer, + SILC_BUFFER_END(packet->packetdata->buffer)); + silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, + paddedlen + mac_len); + if (clnt) { + packet->cipher = clnt->receive_key; + packet->hmac = clnt->hmac; + } + + SILC_LOG_HEXDUMP(("Incoming packet, len %d", + packet->packetdata->buffer->len), + packet->packetdata->buffer->data, + packet->packetdata->buffer->len); + + /* Parse the packet with timeout */ + silc_task_register(server->timeout_queue, fd, + silc_server_packet_parse, + (void *)packet, 0, 100000, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + + /* Pull the packet from inbuf thus we'll get the next one + in the inbuf. */ + silc_buffer_pull(sock->inbuf, paddedlen); + if (clnt->hmac) + silc_buffer_pull(sock->inbuf, mac_len); + } + silc_buffer_clear(sock->inbuf); + return; + } else { + SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len), + sock->inbuf->data, sock->inbuf->len); + + SILC_LOG_DEBUG(("Packet from client, length %d", paddedlen)); + + packet = silc_calloc(1, sizeof(*packet)); + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_copy(sock->inbuf); + packet->server = server; + packet->sock = sock; + if (clnt) { + packet->cipher = clnt->receive_key; + packet->hmac = clnt->hmac; + } + silc_buffer_clear(sock->inbuf); + + /* The packet is ready to be parsed now. However, this is a client + connection so we will parse the packet with timeout. */ + silc_task_register(server->timeout_queue, fd, + silc_server_packet_parse, + (void *)packet, 0, 100000, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } + } + + /* Decrypt a packet coming from server connection */ + if (sock->type == SILC_SOCKET_TYPE_SERVER || + sock->type == SILC_SOCKET_TYPE_ROUTER) { + SilcServerList *srvr = (SilcServerList *)sock->user_data; + SilcServerInternalPacket *packet; + int mac_len = 0; + + if (srvr->hmac) + mac_len = srvr->hmac->hash->hash->hash_len; + + if (sock->inbuf->len - 2 > (paddedlen + mac_len)) { + /* Received possibly many packets at once */ + + while(sock->inbuf->len > 0) { + SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen); + if (sock->inbuf->len < paddedlen) { + SILC_LOG_DEBUG(("Received incorrect packet, dropped")); + return; + } + + paddedlen += 2; + packet = silc_calloc(1, sizeof(*packet)); + packet->server = server; + packet->sock = sock; + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len); + silc_buffer_pull_tail(packet->packetdata->buffer, + SILC_BUFFER_END(packet->packetdata->buffer)); + silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, + paddedlen + mac_len); + if (srvr) { + packet->cipher = srvr->receive_key; + packet->hmac = srvr->hmac; + } + + SILC_LOG_HEXDUMP(("Incoming packet, len %d", + packet->packetdata->buffer->len), + packet->packetdata->buffer->data, + packet->packetdata->buffer->len); + + SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", + srvr->server_type == SILC_SERVER ? + "server" : "router", + srvr->server_name, paddedlen)); + + /* Parse it real soon as the packet is from server. */ + silc_task_register(server->timeout_queue, fd, + silc_server_packet_parse, + (void *)packet, 0, 1, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + + /* Pull the packet from inbuf thus we'll get the next one + in the inbuf. */ + silc_buffer_pull(sock->inbuf, paddedlen); + if (srvr->hmac) + silc_buffer_pull(sock->inbuf, mac_len); + } + silc_buffer_clear(sock->inbuf); + return; + } else { + + SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len), + sock->inbuf->data, sock->inbuf->len); + + SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", + srvr->server_type == SILC_SERVER ? + "server" : "router", + srvr->server_name, paddedlen)); + + packet = silc_calloc(1, sizeof(*packet)); + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_copy(sock->inbuf); + packet->server = server; + packet->sock = sock; + if (srvr) { + packet->cipher = srvr->receive_key; + packet->hmac = srvr->hmac; + } + silc_buffer_clear(sock->inbuf); + + /* The packet is ready to be parsed now. However, this is a client + connection so we will parse the packet with timeout. */ + silc_task_register(server->timeout_queue, fd, + silc_server_packet_parse, + (void *)packet, 0, 1, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } + } + + /* Decrypt a packet coming from client. */ + if (sock->type == SILC_SOCKET_TYPE_UNKNOWN) { + SilcIDListUnknown *conn_data = (SilcIDListUnknown *)sock->user_data; + SilcServerInternalPacket *packet; + + SILC_LOG_HEXDUMP(("Incoming packet, len %d", sock->inbuf->len), + sock->inbuf->data, sock->inbuf->len); + + SILC_LOG_DEBUG(("Packet from unknown connection, length %d", + paddedlen)); + + packet = silc_calloc(1, sizeof(*packet)); + packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata)); + packet->packetdata->buffer = silc_buffer_copy(sock->inbuf); + packet->server = server; + packet->sock = sock; + if (conn_data) { + packet->cipher = conn_data->receive_key; + packet->hmac = conn_data->hmac; + } + + silc_buffer_clear(sock->inbuf); + + /* The packet is ready to be parsed now. However, this is unknown + connection so we will parse the packet with timeout. */ + silc_task_register(server->timeout_queue, fd, + silc_server_packet_parse, + (void *)packet, 0, 100000, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } + } + + SILC_LOG_ERROR(("Weird, nothing happened - ignoring")); +} + +/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called + after packet has been totally decrypted and parsed. */ + +static int silc_server_packet_check_mac(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcHmac hmac = NULL; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned int mac_len = 0; + + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + if (sock->user_data) { + hmac = ((SilcClientList *)sock->user_data)->hmac; + hmac_key = ((SilcClientList *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len; + } + break; + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + if (sock->user_data) { + hmac = ((SilcServerList *)sock->user_data)->hmac; + hmac_key = ((SilcServerList *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len; + } + break; + default: + if (sock->user_data) { + hmac = ((SilcIDListUnknown *)sock->user_data)->hmac; + hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len; + } + } + + /* Check MAC */ + if (hmac) { + int headlen = buffer->data - buffer->head; + unsigned char *packet_mac, mac[32]; + + SILC_LOG_DEBUG(("Verifying MAC")); + + mac_len = hmac->hash->hash->hash_len; + + silc_buffer_push(buffer, headlen); + + /* Take mac from packet */ + packet_mac = buffer->tail; + + /* Make MAC and compare */ + memset(mac, 0, sizeof(mac)); + silc_hmac_make_with_key(hmac, + buffer->data, buffer->len, + hmac_key, hmac_key_len, mac); +#if 0 + SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len); + SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len); +#endif + if (memcmp(mac, packet_mac, mac_len)) { + SILC_LOG_DEBUG(("MAC failed")); + return FALSE; + } + + SILC_LOG_DEBUG(("MAC is Ok")); + memset(mac, 0, sizeof(mac)); + + silc_buffer_pull(buffer, headlen); + } + + return TRUE; +} + +/* Decrypts rest of the packet (after decrypting just the SILC header). + After calling this function the packet is ready to be parsed by calling + silc_packet_parse. */ + +static int silc_server_packet_decrypt_rest(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcCipher session_key = NULL; + SilcHmac hmac = NULL; + unsigned int mac_len = 0; + + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + if (sock->user_data) { + session_key = ((SilcClientList *)sock->user_data)->receive_key; + hmac = ((SilcClientList *)sock->user_data)->hmac; + } + break; + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + if (sock->user_data) { + session_key = ((SilcServerList *)sock->user_data)->receive_key; + hmac = ((SilcServerList *)sock->user_data)->hmac; + } + break; + default: + if (sock->user_data) { + session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key; + hmac = ((SilcIDListUnknown *)sock->user_data)->hmac; + } + } + + /* Decrypt */ + if (session_key) { + + /* Pull MAC from packet before decryption */ + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) { + silc_buffer_push_tail(buffer, mac_len); + } else { + SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped")); + return FALSE; + } + } + + SILC_LOG_DEBUG(("Decrypting rest of the packet")); + + /* Decrypt rest of the packet */ + silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + silc_packet_decrypt(session_key, buffer, buffer->len); + silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + + SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len), + buffer->data, buffer->len); + } + + return TRUE; +} + +/* Decrypts rest of the SILC Packet header that has been decrypted partly + already. This decrypts the padding of the packet also. After calling + this function the packet is ready to be parsed by calling function + silc_packet_parse. */ + +static int silc_server_packet_decrypt_rest_special(SilcServer server, + SilcSocketConnection sock, + SilcBuffer buffer) +{ + SilcCipher session_key = NULL; + SilcHmac hmac = NULL; + unsigned int mac_len = 0; + + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + if (sock->user_data) { + session_key = ((SilcClientList *)sock->user_data)->receive_key; + hmac = ((SilcClientList *)sock->user_data)->hmac; + } + break; + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + if (sock->user_data) { + session_key = ((SilcServerList *)sock->user_data)->receive_key; + hmac = ((SilcServerList *)sock->user_data)->hmac; + } + break; + default: + if (sock->user_data) { + session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key; + hmac = ((SilcIDListUnknown *)sock->user_data)->hmac; + } + } + + /* Decrypt rest of the header plus padding */ + if (session_key) { + unsigned short truelen, len1, len2, padlen; + + /* Pull MAC from packet before decryption */ + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) { + silc_buffer_push_tail(buffer, mac_len); + } else { + SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped")); + return FALSE; + } + } + + SILC_LOG_DEBUG(("Decrypting rest of the header")); + + SILC_GET16_MSB(len1, &buffer->data[4]); + SILC_GET16_MSB(len2, &buffer->data[6]); + + truelen = SILC_PACKET_HEADER_LEN + len1 + len2; + padlen = SILC_PACKET_PADLEN(truelen); + len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2); + + silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + silc_packet_decrypt(session_key, buffer, len1); + silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2); + } + + return TRUE; +} + +/* Parses whole packet, received earlier. This packet is usually received + from client. */ + +SILC_TASK_CALLBACK(silc_server_packet_parse) +{ + SilcServerInternalPacket *packet = (SilcServerInternalPacket *)context; + SilcServer server = packet->server; + SilcSocketConnection sock = packet->sock; + SilcBuffer buffer = packet->packetdata->buffer; + int ret; + + SILC_LOG_DEBUG(("Start")); + + /* Decrypt start of the packet header */ + if (packet->cipher) + silc_packet_decrypt(packet->cipher, buffer, SILC_PACKET_MIN_HEADER_LEN); + + /* If the packet type is not any special type lets decrypt rest + of the packet here. */ + if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE && + buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) { + normal: + /* Normal packet, decrypt rest of the packet */ + if (!silc_server_packet_decrypt_rest(server, sock, buffer)) + goto out; + + /* Parse the packet. Packet type is returned. */ + ret = silc_packet_parse(packet->packetdata); + if (ret == SILC_PACKET_NONE) + goto out; + + /* Check MAC */ + if (!silc_server_packet_check_mac(server, sock, buffer)) + goto out; + } else { + /* If private message key is not set for private message it is + handled as normal packet. Go back up. */ + if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE && + !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) + goto normal; + + /* Packet requires special handling, decrypt rest of the header. + This only decrypts. This does not do any MAC checking, it must + be done individually later when doing the special processing. */ + silc_server_packet_decrypt_rest_special(server, sock, buffer); + + /* Parse the packet header in special way as this is "special" + packet type. */ + ret = silc_packet_parse_special(packet->packetdata); + if (ret == SILC_PACKET_NONE) + goto out; + } + + /* Parse the incoming packet type */ + silc_server_packet_parse_type(server, sock, packet->packetdata); + + out: + silc_buffer_clear(sock->inbuf); + // silc_buffer_free(packetdata->packetdata->buffer); + silc_free(packet->packetdata); + silc_free(packet); +} + +/* Parses the packet type and calls what ever routines the packet type + requires. This is done for all incoming packets. */ + +void silc_server_packet_parse_type(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcPacketType type = packet->type; + + SILC_LOG_DEBUG(("Parsing packet type %d", type)); + + /* Parse the packet type */ + switch(type) { + case SILC_PACKET_DISCONNECT: + SILC_LOG_DEBUG(("Disconnect packet")); + break; + case SILC_PACKET_SUCCESS: + /* + * Success received for something. For now we can have only + * one protocol for connection executing at once hence this + * success message is for whatever protocol is executing currently. + */ + SILC_LOG_DEBUG(("Success packet")); + if (sock->protocol) { + sock->protocol->execute(server->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); + } + break; + case SILC_PACKET_FAILURE: + SILC_LOG_DEBUG(("Failure packet")); + break; + case SILC_PACKET_REJECT: + SILC_LOG_DEBUG(("Reject packet")); + return; + break; + + /* + * Channel packets + */ + case SILC_PACKET_CHANNEL_MESSAGE: + /* + * Received channel message. Channel messages are special packets + * (although probably most common ones) hence they are handled + * specially. + */ + SILC_LOG_DEBUG(("Channel Message packet")); + silc_server_channel_message(server, sock, packet); + break; + + case SILC_PACKET_CHANNEL_KEY: + /* + * Received key for channel. As channels are created by the router + * the keys are as well. We will distribute the key to all of our + * locally connected clients on the particular channel. Router + * never receives this channel and thus is ignored. + */ + SILC_LOG_DEBUG(("Channel Key packet")); + silc_server_channel_key(server, sock, packet); + break; + + /* + * Command packets + */ + case SILC_PACKET_COMMAND: + { + /* + * Recived command. Allocate command context and execute the command. + */ + SilcServerCommandContext ctx; + + SILC_LOG_DEBUG(("Command packet")); + + /* Router cannot send command packet */ + if (sock->type == SILC_SOCKET_TYPE_ROUTER) + break; + + /* Allocate command context. This must be free'd by the + command routine receiving it. */ + ctx = silc_calloc(1, sizeof(*ctx)); + ctx->server = server; + ctx->sock = sock; + ctx->packet = packet; /* Save original packet */ + + /* Parse the command payload in the packet */ + ctx->payload = silc_command_parse_payload(buffer); + if (!ctx->payload) { + SILC_LOG_ERROR(("Bad command payload, packet dropped")); + silc_free(ctx); + return; + } + + /* Execute command. If this fails the packet is dropped. */ + SILC_SERVER_COMMAND_EXEC(ctx); + silc_buffer_free(buffer); + } + break; + + case SILC_PACKET_COMMAND_REPLY: + /* + * Received command reply packet. Servers never send commands thus + * they don't receive command reply packets either, except in cases + * where server has forwarded command packet coming from client. + * This must be the case here or we will ignore the packet. + */ + SILC_LOG_DEBUG(("Command Reply packet")); + silc_server_packet_relay_command_reply(server, sock, packet); + break; + + /* + * Private Message packets + */ + case SILC_PACKET_PRIVATE_MESSAGE: + /* + * Received private message packet. The packet is coming from either + * client or server. + */ + SILC_LOG_DEBUG(("Private Message packet")); + silc_server_private_message(server, sock, packet); + break; + + case SILC_PACKET_PRIVATE_MESSAGE_KEY: + break; + + /* + * Key Exchange protocol packets + */ + case SILC_PACKET_KEY_EXCHANGE: + SILC_LOG_DEBUG(("KE packet")); + if (sock->protocol && sock->protocol->protocol->type + == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) { + + SilcServerKEInternalContext *proto_ctx = + (SilcServerKEInternalContext *)sock->protocol->context; + + proto_ctx->packet = buffer; + + /* Let the protocol handle the packet */ + sock->protocol->execute(server->timeout_queue, 0, + sock->protocol, sock->sock, 0, 100000); + } else { + SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange " + "protocol active, packet dropped.")); + + /* XXX Trigger KE protocol?? Rekey actually, maybe. */ + } + break; + + case SILC_PACKET_KEY_EXCHANGE_1: + SILC_LOG_DEBUG(("KE 1 packet")); + if (sock->protocol && sock->protocol->protocol->type + == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) { + + SilcServerKEInternalContext *proto_ctx = + (SilcServerKEInternalContext *)sock->protocol->context; + + if (proto_ctx->packet) + silc_buffer_free(proto_ctx->packet); + + proto_ctx->packet = buffer; + proto_ctx->dest_id_type = packet->src_id_type; + proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type); + + /* Let the protocol handle the packet */ + sock->protocol->execute(server->timeout_queue, 0, + sock->protocol, sock->sock, + 0, 100000); + } else { + SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange " + "protocol active, packet dropped.")); + } + break; + + case SILC_PACKET_KEY_EXCHANGE_2: + SILC_LOG_DEBUG(("KE 2 packet")); + if (sock->protocol && sock->protocol->protocol->type + == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) { + + SilcServerKEInternalContext *proto_ctx = + (SilcServerKEInternalContext *)sock->protocol->context; + + if (proto_ctx->packet) + silc_buffer_free(proto_ctx->packet); + + proto_ctx->packet = buffer; + proto_ctx->dest_id_type = packet->src_id_type; + proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type); + + /* Let the protocol handle the packet */ + sock->protocol->execute(server->timeout_queue, 0, + sock->protocol, sock->sock, + 0, 100000); + } else { + SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange " + "protocol active, packet dropped.")); + } + break; + + case SILC_PACKET_CONNECTION_AUTH_REQUEST: + /* If we receive this packet we will send to the other end information + about our mandatory authentication method for the connection. + This packet maybe received at any time. */ + + /* + * Connection Authentication protocol packets + */ + case SILC_PACKET_CONNECTION_AUTH: + /* Start of the authentication protocol. We receive here the + authentication data and will verify it. */ + SILC_LOG_DEBUG(("Connection auth packet")); + if (sock->protocol && sock->protocol->protocol->type + == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) { + + SilcServerConnAuthInternalContext *proto_ctx = + (SilcServerConnAuthInternalContext *)sock->protocol->context; + + proto_ctx->packet = buffer; + + /* Let the protocol handle the packet */ + sock->protocol->execute(server->timeout_queue, 0, + sock->protocol, sock->sock, 0, 0); + } else { + SILC_LOG_ERROR(("Received Connection Auth packet but no authentication " + "protocol active, packet dropped.")); + } + break; + + case SILC_PACKET_NEW_ID: + /* + * Received New ID packet. This includes some new ID that has been + * created. It may be for client, server or channel. This is the way + * to distribute information about new registered entities in the + * SILC network. + */ + SILC_LOG_DEBUG(("New ID packet")); + silc_server_new_id(server, sock, packet); + break; + + case SILC_PACKET_NEW_CLIENT: + /* + * Received new client packet. This includes client information that + * we will use to create initial client ID. After creating new + * ID we will send it to the client. + */ + SILC_LOG_DEBUG(("New Client packet")); + silc_server_new_client(server, sock, packet); + break; + + case SILC_PACKET_NEW_SERVER: + /* + * Received new server packet. This includes Server ID and some other + * information that we may save. This is after server as connected to us. + */ + SILC_LOG_DEBUG(("New Server packet")); + silc_server_new_server(server, sock, packet); + break; + + default: + SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type)); + break; + } + +} + +/* Internal routine that sends packet or marks packet to be sent. This + is used directly only in special cases. Normal cases should use + silc_server_packet_send. Returns < 0 error. */ + +static int silc_server_packet_send_real(SilcServer server, + SilcSocketConnection sock, + int force_send) +{ + /* Send now if forced to do so */ + if (force_send == TRUE) { + int ret; + SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately")); + ret = silc_packet_write(sock->sock, sock->outbuf); + + silc_buffer_clear(sock->outbuf); + + if (ret == -1) + SILC_LOG_ERROR(("Could not write, packet dropped")); + if (ret != -2) { + silc_buffer_clear(sock->outbuf); + return ret; + } + + SILC_LOG_DEBUG(("Could not force the send, packet put to queue")); + } + + SILC_LOG_DEBUG(("Packet in queue")); + + /* Mark that there is some outgoing data available for this connection. + This call sets the connection both for input and output (the input + is set always and this call keeps the input setting, actually). + Actual data sending is performed by silc_server_packet_process. */ + SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock); + + /* Mark to socket that data is pending in outgoing buffer. This flag + is needed if new data is added to the buffer before the earlier + put data is sent to the network. */ + SILC_SET_OUTBUF_PENDING(sock); + + return 0; +} + +/* Prepare outgoing data buffer for packet sending. This is internal + routine and must always be called before sending any packets out. */ + +static void silc_server_packet_send_prepare(SilcServer server, + SilcSocketConnection sock, + unsigned int header_len, + unsigned int padlen, + unsigned int data_len) +{ + int totlen, oldlen; + + totlen = header_len + padlen + data_len; + + /* 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")); + + sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE); + silc_buffer_pull_tail(sock->outbuf, totlen); + silc_buffer_pull(sock->outbuf, header_len + padlen); + } else { + if (SILC_IS_OUTBUF_PENDING(sock)) { + /* There is some pending data in the buffer. */ + + if ((sock->outbuf->end - sock->outbuf->tail) < data_len) { + SILC_LOG_DEBUG(("Reallocating outgoing data buffer")); + /* XXX: not done yet */ + } + oldlen = sock->outbuf->len; + silc_buffer_pull_tail(sock->outbuf, totlen); + silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen); + } else { + /* Buffer is free for use */ + silc_buffer_clear(sock->outbuf); + silc_buffer_pull_tail(sock->outbuf, totlen); + silc_buffer_pull(sock->outbuf, header_len + padlen); + } + } +} + +/* Assembles a new packet to be sent out to network. This doesn't actually + send the packet but creates the packet and fills the outgoing data + buffer and marks the packet ready to be sent to network. However, If + argument force_send is TRUE the packet is sent immediately and not put + to queue. Normal case is that the packet is not sent immediately. */ + +void silc_server_packet_send(SilcServer server, + SilcSocketConnection sock, + SilcPacketType type, + SilcPacketFlags flags, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + void *dst_id = NULL; + SilcIdType dst_id_type = SILC_ID_NONE; + + /* Get data used in the packet sending, keys and stuff */ + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + if (((SilcClientList *)sock->user_data)->id) { + dst_id = ((SilcClientList *)sock->user_data)->id; + dst_id_type = SILC_ID_CLIENT; + } + break; + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + if (((SilcServerList *)sock->user_data)->id) { + dst_id = ((SilcServerList *)sock->user_data)->id; + dst_id_type = SILC_ID_SERVER; + } + break; + default: + break; + } + + silc_server_packet_send_dest(server, sock, type, flags, dst_id, + dst_id_type, data, data_len, force_send); +} + +/* Assembles a new packet to be sent out to network. This doesn't actually + send the packet but creates the packet and fills the outgoing data + buffer and marks the packet ready to be sent to network. However, If + argument force_send is TRUE the packet is sent immediately and not put + to queue. Normal case is that the packet is not sent immediately. + Destination information is sent as argument for this function. */ + +void silc_server_packet_send_dest(SilcServer server, + SilcSocketConnection sock, + SilcPacketType type, + SilcPacketFlags flags, + void *dst_id, + SilcIdType dst_id_type, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + SilcPacketContext packetdata; + SilcCipher cipher = NULL; + SilcHmac hmac = NULL; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + unsigned char *dst_id_data = NULL; + unsigned int dst_id_len = 0; + + SILC_LOG_DEBUG(("Sending packet, type %d", type)); + + /* Get data used in the packet sending, keys and stuff */ + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + if (sock->user_data) { + cipher = ((SilcClientList *)sock->user_data)->send_key; + hmac = ((SilcClientList *)sock->user_data)->hmac; + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + hmac_key = ((SilcClientList *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len; + } + } + break; + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + if (sock->user_data) { + cipher = ((SilcServerList *)sock->user_data)->send_key; + hmac = ((SilcServerList *)sock->user_data)->hmac; + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + hmac_key = ((SilcServerList *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len; + } + } + break; + default: + if (sock->user_data) { + /* We don't know what type of connection this is thus it must + be in authentication phase. */ + cipher = ((SilcIDListUnknown *)sock->user_data)->send_key; + hmac = ((SilcIDListUnknown *)sock->user_data)->hmac; + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len; + } + } + break; + } + + if (dst_id) { + dst_id_data = silc_id_id2str(dst_id, dst_id_type); + dst_id_len = silc_id_get_len(dst_id_type); + } + + /* Set the packet context pointers */ + packetdata.type = type; + packetdata.flags = flags; + packetdata.src_id = silc_id_id2str(server->id, server->id_type); + packetdata.src_id_len = SILC_ID_SERVER_LEN; + packetdata.src_id_type = server->id_type; + packetdata.dst_id = dst_id_data; + packetdata.dst_id_len = dst_id_len; + packetdata.dst_id_type = dst_id_type; + packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + dst_id_len; + packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen); + packetdata.rng = server->rng; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + data_len); + + SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len)); + + packetdata.buffer = sock->outbuf; + + /* Put the data to the buffer */ + if (data && data_len) + silc_buffer_put(sock->outbuf, data, data_len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet */ + if (hmac) { + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + } + + /* Encrypt the packet */ + if (cipher) + silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len); + + /* Pull MAC into the visible data area */ + if (hmac) + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + + if (packetdata.src_id) + silc_free(packetdata.src_id); + if (packetdata.dst_id) + silc_free(packetdata.dst_id); +} + +/* Forwards packet. Packets sent with this function will be marked as + forwarded (in the SILC header flags) so that the receiver knows that + we have forwarded the packet to it. Forwarded packets are handled + specially by the receiver as they are not destined to the receiver + originally. However, the receiver knows this because the forwarded + flag has been set (and the flag is authenticated). */ + +void silc_server_packet_forward(SilcServer server, + SilcSocketConnection sock, + unsigned char *data, unsigned int data_len, + int force_send) +{ + SilcCipher cipher = NULL; + SilcHmac hmac = NULL; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + + SILC_LOG_DEBUG(("Forwarding packet")); + + /* Get data used in the packet sending, keys and stuff */ + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + if (sock->user_data) { + cipher = ((SilcClientList *)sock->user_data)->send_key; + hmac = ((SilcClientList *)sock->user_data)->hmac; + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + hmac_key = ((SilcClientList *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len; + } + } + break; + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + if (sock->user_data) { + cipher = ((SilcServerList *)sock->user_data)->send_key; + hmac = ((SilcServerList *)sock->user_data)->hmac; + if (hmac) { + mac_len = hmac->hash->hash->hash_len; + hmac_key = ((SilcServerList *)sock->user_data)->hmac_key; + hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len; + } + } + break; + default: + /* We won't forward to unknown destination - keys must exist with + the destination before forwarding. */ + return; + } + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, 0, 0, data_len); + + /* Mungle the packet flags and add the FORWARDED flag */ + if (data) + data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED; + + /* Put the data to the buffer */ + if (data && data_len) + silc_buffer_put(sock->outbuf, data, data_len); + + /* Compute MAC of the packet */ + if (hmac) { + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + } + + /* Encrypt the packet */ + if (cipher) + silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len); + + /* Pull MAC into the visible data area */ + if (hmac) + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); +} + +/* This routine is used by the server to send packets to channel. The + packet sent with this function is distributed to all clients on + the channel. Usually this is used to send notify messages to the + channel, things like notify about new user joining to the channel. */ + +void silc_server_packet_send_to_channel(SilcServer server, + SilcChannelList *channel, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + int i; + SilcSocketConnection sock = NULL; + SilcPacketContext packetdata; + SilcClientList *client = NULL; + SilcServerList **routed = NULL; + unsigned int routed_count = 0; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + SilcCipher cipher; + SilcHmac hmac; + SilcBuffer payload; + + SILC_LOG_DEBUG(("Sending packet to channel")); + + /* Generate IV */ + for (i = 0; i < 16; i++) + channel->iv[i] = silc_rng_get_byte(server->rng); + + /* Encode the channel payload */ + payload = silc_channel_encode_payload(0, "", data_len, data, + 16, channel->iv, server->rng); + if (!payload) + return; + + /* Encrypt payload of the packet. This is encrypted with the + channel key. */ + channel->channel_key->cipher->encrypt(channel->channel_key->context, + payload->data, payload->data, + payload->len - 16, /* -IV_LEN */ + channel->iv); + + /* Set the packet context pointers. */ + packetdata.flags = 0; + packetdata.type = SILC_PACKET_CHANNEL_MESSAGE; + packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER); + packetdata.src_id_len = SILC_ID_SERVER_LEN; + packetdata.src_id_type = SILC_ID_SERVER; + packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + packetdata.dst_id_len = SILC_ID_CHANNEL_LEN; + packetdata.dst_id_type = SILC_ID_CHANNEL; + packetdata.rng = server->rng; + packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len)); + + /* If there are global users in the channel we will send the message + first to our router for further routing. */ + if (server->server_type == SILC_SERVER && !server->standalone && + channel->global_users) { + SilcServerList *router; + + /* Get data used in packet header encryption, keys and stuff. */ + router = server->id_entry->router; + sock = (SilcSocketConnection)router->connection; + cipher = router->send_key; + hmac = router->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = router->hmac_key; + hmac_key_len = router->hmac_key_len; + + SILC_LOG_DEBUG(("Sending packet to router for routing")); + + packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + payload->len); + packetdata.buffer = sock->outbuf; + + /* Put the original packet into the buffer. */ + silc_buffer_put(sock->outbuf, payload->data, payload->len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet. MAC is computed from the header, + padding and the relayed packet. */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with the client. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + } + + /* Send the message to clients on the channel's client list. */ + for (i = 0; i < channel->user_list_count; i++) { + client = channel->user_list[i].client; + + /* If client has router set it is not locally connected client and + we will route the message to the router set in the client. */ + if (client && client->router && server->server_type == SILC_ROUTER) { + int k; + + /* Check if we have sent the packet to this route already */ + for (k = 0; k < routed_count; k++) + if (routed[k] == client->router) + continue; + + /* Get data used in packet header encryption, keys and stuff. */ + sock = (SilcSocketConnection)client->router->connection; + cipher = client->router->send_key; + hmac = client->router->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = client->router->hmac_key; + hmac_key_len = client->router->hmac_key_len; + + packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + payload->len); + packetdata.buffer = sock->outbuf; + + /* Put the encrypted payload data into the buffer. */ + silc_buffer_put(sock->outbuf, payload->data, payload->len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet. MAC is computed from the header, + padding and the relayed packet. */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with the client. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + + /* We want to make sure that the packet is routed to same router + only once. Mark this route as sent route. */ + k = routed_count; + routed = silc_realloc(routed, sizeof(*routed) * (k + 1)); + routed[k] = client->router; + routed_count++; + + continue; + } + + /* Send to locally connected client */ + if (client) { + + /* XXX Check client's mode on the channel. */ + + /* Get data used in packet header encryption, keys and stuff. */ + sock = (SilcSocketConnection)client->connection; + cipher = client->send_key; + hmac = client->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = client->hmac_key; + hmac_key_len = client->hmac_key_len; + + packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + payload->len); + packetdata.buffer = sock->outbuf; + + /* Put the encrypted payload data into the buffer. */ + silc_buffer_put(sock->outbuf, payload->data, payload->len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet. MAC is computed from the header, + padding and the relayed packet. */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with the client. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + } + } + + if (routed_count) + silc_free(routed); + silc_free(packetdata.src_id); + silc_free(packetdata.dst_id); + silc_buffer_free(payload); +} + +/* This routine is explicitly used to relay messages to some channel. + Packets sent with this function we have received earlier and are + totally encrypted. This just sends the packet to all clients on + the channel. If the sender of the packet is someone on the channel + the message will not be sent to that client. The SILC Packet header + is encrypted with the session key shared between us and the client. + MAC is also computed before encrypting the header. Rest of the + packet will be untouched. */ + +void silc_server_packet_relay_to_channel(SilcServer server, + SilcSocketConnection sender_sock, + SilcChannelList *channel, + void *sender, + SilcIdType sender_type, + unsigned char *data, + unsigned int data_len, + int force_send) +{ + int i, found = FALSE; + SilcSocketConnection sock = NULL; + SilcPacketContext packetdata; + SilcClientList *client = NULL; + SilcServerList **routed = NULL; + unsigned int routed_count = 0; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_len = 0; + unsigned char mac[32]; + unsigned int mac_len = 0; + SilcCipher cipher; + SilcHmac hmac; + + SILC_LOG_DEBUG(("Relaying packet to channel")); + + SILC_LOG_HEXDUMP(("XXX %d", data_len), data, data_len); + + /* Set the packet context pointers. */ + packetdata.flags = 0; + packetdata.type = SILC_PACKET_CHANNEL_MESSAGE; + packetdata.src_id = silc_id_id2str(sender, sender_type); + packetdata.src_id_len = silc_id_get_len(sender_type); + packetdata.src_id_type = sender_type; + packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL); + packetdata.dst_id_len = SILC_ID_CHANNEL_LEN; + packetdata.dst_id_type = SILC_ID_CHANNEL; + packetdata.rng = server->rng; + packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len)); + + /* If there are global users in the channel we will send the message + first to our router for further routing. */ + if (server->server_type == SILC_SERVER && !server->standalone && + channel->global_users) { + SilcServerList *router; + + router = server->id_entry->router; + + /* Check that the sender is not our router. */ + if (sender_sock != (SilcSocketConnection)router->connection) { + + /* Get data used in packet header encryption, keys and stuff. */ + sock = (SilcSocketConnection)router->connection; + cipher = router->send_key; + hmac = router->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = router->hmac_key; + hmac_key_len = router->hmac_key_len; + + SILC_LOG_DEBUG(("Sending packet to router for routing")); + + packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + data_len); + packetdata.buffer = sock->outbuf; + + /* Put the original packet into the buffer. */ + silc_buffer_put(sock->outbuf, data, data_len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet. MAC is computed from the header, + padding and the relayed packet. */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with the client. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + } + } + + /* Send the message to clients on the channel's client list. */ + for (i = 0; i < channel->user_list_count; i++) { + client = channel->user_list[i].client; + + if (client) { + + /* If sender is one on the channel do not send it the packet. */ + if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) { + found = TRUE; + continue; + } + + /* If the client has set router it means that it is not locally + connected client and we won't send message to those in this + function (they will be routed separately by the caller). */ + if (server->server_type == SILC_ROUTER && client->router) { + int k; + + /* Sender maybe server as well so we want to make sure that + we won't send the message to the server it came from. */ + if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) { + found = TRUE; + continue; + } + + /* Check if we have sent the packet to this route already */ + for (k = 0; k < routed_count; k++) + if (routed[k] == client->router) + continue; + + /* Get data used in packet header encryption, keys and stuff. */ + sock = (SilcSocketConnection)client->router->connection; + cipher = client->router->send_key; + hmac = client->router->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = client->router->hmac_key; + hmac_key_len = client->router->hmac_key_len; + + packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + data_len); + packetdata.buffer = sock->outbuf; + + /* Put the original packet into the buffer. */ + silc_buffer_put(sock->outbuf, data, data_len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet. MAC is computed from the header, + padding and the relayed packet. */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with the client. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + + /* We want to make sure that the packet is routed to same router + only once. Mark this route as sent route. */ + k = routed_count; + routed = silc_realloc(routed, sizeof(*routed) * (k + 1)); + routed[k] = client->router; + routed_count++; + + continue; + } + + /* XXX Check client's mode on the channel. */ + + + /* Get data used in packet header encryption, keys and stuff. */ + sock = (SilcSocketConnection)client->connection; + cipher = client->send_key; + hmac = client->hmac; + mac_len = hmac->hash->hash->hash_len; + hmac_key = client->hmac_key; + hmac_key_len = client->hmac_key_len; + + SILC_LOG_DEBUG(("Sending packet to client %s", + sock->hostname ? sock->hostname : sock->ip)); + + packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len; + + /* Prepare outgoing data buffer for packet sending */ + silc_server_packet_send_prepare(server, sock, + SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + + packetdata.dst_id_len, + packetdata.padlen, + data_len); + packetdata.buffer = sock->outbuf; + + /* Put the original packet into the buffer. */ + silc_buffer_put(sock->outbuf, data, data_len); + + /* Create the outgoing packet */ + silc_packet_assemble(&packetdata); + + /* Compute MAC of the packet. MAC is computed from the header, + padding and the relayed packet. */ + silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len, + hmac_key, hmac_key_len, mac); + silc_buffer_put_tail(sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + + /* Encrypt the header and padding of the packet. This is encrypted + with normal session key shared with the client. */ + silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + + packetdata.src_id_len + packetdata.dst_id_len + + packetdata.padlen); + + /* Pull MAC into the visible data area */ + silc_buffer_pull_tail(sock->outbuf, mac_len); + + SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len), + sock->outbuf->data, sock->outbuf->len); + + /* Now actually send the packet */ + silc_server_packet_send_real(server, sock, force_send); + } + } + + silc_free(packetdata.src_id); + silc_free(packetdata.dst_id); +} + +/* Relays received command reply packet to the correct destination. The + destination must be one of our locally connected client or the packet + will be ignored. This is called when server has forwarded one of + client's command request to router and router has now replied to the + command. */ + +void silc_server_packet_relay_command_reply(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcClientList *client; + SilcClientID *id; + SilcSocketConnection dst_sock; + unsigned char mac[32]; + unsigned int mac_len = 0; + + SILC_LOG_DEBUG(("Start")); + + /* Source must be server or router */ + /* XXX: actually it must be only router */ + if (packet->src_id_type != SILC_ID_SERVER && + (sock->type != SILC_SOCKET_TYPE_SERVER || + sock->type != SILC_SOCKET_TYPE_ROUTER)) + goto out; + + /* Destination must be client */ + if (packet->dst_id_type != SILC_ID_CLIENT) + goto out; + + /* Execute command reply locally for the command */ + silc_server_command_reply_process(server, sock, buffer); + + id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT); + + /* Destination must be one of ours */ + client = silc_idlist_find_client_by_id(server->local_list->clients, id); + if (!client) { + silc_free(id); + goto out; + } + + /* Relay the packet to the client */ + if (client->hmac) + mac_len = client->hmac->hash->hash->hash_len; + + dst_sock = (SilcSocketConnection)client->connection; + + silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len); + silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); + + /* Compute new HMAC */ + if (client->hmac) { + memset(mac, 0, sizeof(mac)); + silc_hmac_make_with_key(client->hmac, + dst_sock->outbuf->data, + dst_sock->outbuf->len, + client->hmac_key, + client->hmac_key_len, + mac); + silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + } + + /* Encrypt */ + if (client && client->send_key) + silc_packet_encrypt(client->send_key, dst_sock->outbuf, buffer->len); + + if (client->hmac) + silc_buffer_pull_tail(dst_sock->outbuf, mac_len); + + /* Send the packet */ + silc_server_packet_send_real(server, dst_sock, FALSE); + + silc_free(id); + + out: + silc_buffer_free(buffer); +} + +/* Closes connection to socket connection */ + +void silc_server_close_connection(SilcServer server, + SilcSocketConnection sock) +{ + + SILC_LOG_DEBUG(("Closing connection %d", sock->sock)); + + /* We won't listen for this connection anymore */ + silc_schedule_unset_listen_fd(sock->sock); + + /* Unregister all tasks */ + silc_task_unregister_by_fd(server->io_queue, sock->sock); + silc_task_unregister_by_fd(server->timeout_queue, sock->sock); + + /* Close the actual connection */ + silc_net_close_connection(sock->sock); + server->sockets[sock->sock] = NULL; + silc_socket_free(sock); +} + +/* Sends disconnect message to remote connection and disconnects the + connection. */ + +void silc_server_disconnect_remote(SilcServer server, + SilcSocketConnection sock, + const char *fmt, ...) +{ + va_list ap; + unsigned char buf[4096]; + + memset(buf, 0, sizeof(buf)); + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + SILC_LOG_DEBUG(("Disconnecting remote host")); + + /* Notify remote end that the conversation is over. The notify message + is tried to be sent immediately. */ + silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0, + buf, strlen(buf), TRUE); + + /* Mark the connection to be disconnected */ + SILC_SET_DISCONNECTED(sock); + silc_server_close_connection(server, sock); +} + +/* Free's user_data pointer from socket connection object. As this + pointer maybe anything we wil switch here to find the corrent + data type and free it the way it needs to be free'd. */ + +void silc_server_free_sock_user_data(SilcServer server, + SilcSocketConnection sock) +{ + SILC_LOG_DEBUG(("Start")); + +#define LCC(x) server->local_list->client_cache[(x) - 32] +#define LCCC(x) server->local_list->client_cache_count[(x) - 32] + + switch(sock->type) { + case SILC_SOCKET_TYPE_CLIENT: + { + SilcClientList *user_data = (SilcClientList *)sock->user_data; + + /* Remove client from all channels */ + silc_server_remove_from_channels(server, sock, user_data); + + /* Clear ID cache */ + if (user_data->nickname && user_data->id) + silc_idcache_del_by_id(LCC(user_data->nickname[0]), + LCCC(user_data->nickname[0]), + SILC_ID_CLIENT, user_data->id); + + /* Free the client entry and everything in it */ + /* XXX must take some info to history before freeing */ + silc_idlist_del_client(&server->local_list->clients, user_data); + break; + } + case SILC_SOCKET_TYPE_SERVER: + case SILC_SOCKET_TYPE_ROUTER: + { + + break; + } + break; + default: + { + SilcIDListUnknown *user_data = (SilcIDListUnknown *)sock->user_data; + + if (user_data->send_key) + silc_cipher_free(user_data->send_key); + if (user_data->receive_key) + silc_cipher_free(user_data->receive_key); + if (user_data->pkcs) + silc_pkcs_free(user_data->pkcs); + if (user_data->hmac) { + silc_hmac_free(user_data->hmac); + memset(user_data->hmac_key, 0, user_data->hmac_key_len); + silc_free(user_data->hmac_key); + } + silc_free(user_data); + break; + } + } + + sock->user_data = NULL; +#undef LCC +#undef LCCC +} + +/* Removes client from all channels it has joined. This is used when + client connection is disconnected. If the client on a channel + is last, the channel is removed as well. */ + +void silc_server_remove_from_channels(SilcServer server, + SilcSocketConnection sock, + SilcClientList *client) +{ + int i, k; + SilcChannelList *channel; + +#define LCC(x) server->local_list->channel_cache[(x) - 32] +#define LCCC(x) server->local_list->channel_cache_count[(x) - 32] + + /* Remove the client from all channels. The client is removed from + the channels' user list. */ + for (i = 0; i < client->channel_count; i++) { + channel = client->channel[i]; + if (!channel) + continue; + + /* Remove from channel */ + for (k = 0; k < channel->user_list_count; k++) { + if (channel->user_list[k].client == client) { + + /* If this client is last one on the channel the channel + is removed all together. */ + if (channel->user_list_count == 1) { + silc_idcache_del_by_id(LCC(channel->channel_name[0]), + LCCC(channel->channel_name[0]), + SILC_ID_CHANNEL, channel->id); + silc_idlist_del_channel(&server->local_list->channels, channel); + break; + } + + channel->user_list[k].client = NULL; + channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE; + + /* XXX */ + /* Send notify to channel about client leaving SILC and thus + the entire channel. */ + silc_server_send_notify_to_channel(server, channel, + "%s has left channel %s", + client->nickname, + channel->channel_name); + } + } + } + + if (client->channel_count) + silc_free(client->channel); + client->channel = NULL; +#undef LCC +#undef LCCC +} + +/* Timeout callback. This is called if connection is idle or for some + other reason is not responding within some period of time. This + disconnects the remote end. */ + +SILC_TASK_CALLBACK(silc_server_timeout_remote) +{ + SilcServer server = (SilcServer)context; + SilcSocketConnection sock = server->sockets[fd]; + + silc_server_disconnect_remote(server, sock, + "Server closed connection: " + "Connection timeout"); +} + +/* Internal routine used to send (relay, route) private messages to some + destination. This is used to by normal server to send the message to + its primary route and router uses this to send it to any route it + wants. If the private message key does not exist then the message + is re-encrypted, otherwise we just pass it along. */ +static void +silc_server_private_message_send_internal(SilcServer server, + SilcSocketConnection dst_sock, + SilcServerList *router, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + + /* Send and re-encrypt if private messge key does not exist */ + if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) { + unsigned char mac[32]; + unsigned int mac_len = 0; + + if (router->hmac) + mac_len = router->hmac->hash->hash->hash_len; + + silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len); + silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); + + /* Compute new HMAC */ + if (router->hmac) { + mac_len = router->hmac->hash->hash->hash_len; + memset(mac, 0, sizeof(mac)); + silc_hmac_make_with_key(router->hmac, + dst_sock->outbuf->data, + dst_sock->outbuf->len, + router->hmac_key, + router->hmac_key_len, + mac); + silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + } + + silc_packet_encrypt(router->send_key, dst_sock->outbuf, buffer->len); + + if (router->hmac) + silc_buffer_pull_tail(dst_sock->outbuf, mac_len); + + /* Send the packet */ + silc_server_packet_send_real(server, dst_sock, FALSE); + + } else { + /* Key exist so just send it */ + silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len); + silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); + silc_server_packet_send_real(server, dst_sock, FALSE); + } +} + +/* Internal routine to send the received private message packet to + our locally connected client. */ +static void +silc_server_private_message_send_local(SilcServer server, + SilcSocketConnection dst_sock, + SilcClientList *client, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + + /* Re-encrypt packet if needed */ + if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) { + unsigned char mac[32]; + unsigned int mac_len = 0; + + if (client->hmac) + mac_len = client->hmac->hash->hash->hash_len; + + silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len); + silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); + + /* Compute new HMAC */ + if (client->hmac) { + memset(mac, 0, sizeof(mac)); + silc_hmac_make_with_key(client->hmac, + dst_sock->outbuf->data, + dst_sock->outbuf->len, + client->hmac_key, + client->hmac_key_len, + mac); + silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len); + memset(mac, 0, sizeof(mac)); + } + + /* Encrypt */ + if (client && client->send_key) + silc_packet_encrypt(client->send_key, dst_sock->outbuf, + buffer->len); + + if (client->hmac) + silc_buffer_pull_tail(dst_sock->outbuf, mac_len); + + /* Send the packet */ + silc_server_packet_send_real(server, dst_sock, FALSE); + } else { + /* Key exist so just send it */ + silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len + + packet->dst_id_len + packet->padlen); + silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len); + silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len); + silc_server_packet_send_real(server, dst_sock, FALSE); + } +} + +/* Received private message. This resolves the destination of the message + and sends the packet. This is used by both server and router. If the + destination is our locally connected client this sends the packet to + the client. This may also send the message for further routing if + the destination is not in our server (or router). */ + +void silc_server_private_message(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcClientID *id; + SilcServerList *router; + SilcSocketConnection dst_sock; + SilcClientList *client; + + SILC_LOG_DEBUG(("Start")); + + if (!packet->dst_id) { + SILC_LOG_DEBUG(("Bad Client ID in private message packet")); + goto err; + } + + /* Decode destination Client ID */ + id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT); + if (!id) { + SILC_LOG_DEBUG(("Could not decode destination Client ID")); + goto err; + } + + /* If the destination belongs to our server we don't have to route + the message anywhere but to send it to the local destination. */ + /* XXX: Should use local cache to search but the current idcache system + is so sucky that it cannot be used... it MUST be rewritten! Using + this search is probably faster than if we'd use here the current + idcache system. */ + client = silc_idlist_find_client_by_id(server->local_list->clients, id); + if (client) { + /* It exists, now deliver the message to the destination */ + dst_sock = (SilcSocketConnection)client->connection; + + /* If we are router and the client has router then the client is in + our cell but not directly connected to us. */ + if (server->server_type == SILC_ROUTER && client->router) { + silc_server_private_message_send_internal(server, dst_sock, + client->router, packet); + goto out; + } + + /* Seems that client really is directly connected to us */ + silc_server_private_message_send_local(server, dst_sock, client, packet); + goto out; + } + + /* Destination belongs to someone not in this server. If we are normal + server our action is to send the packet to our router. */ + if (server->server_type == SILC_SERVER && !server->standalone) { + router = server->id_entry->router; + dst_sock = (SilcSocketConnection)router->connection; + + /* Send to primary route */ + silc_server_private_message_send_internal(server, dst_sock, router, + packet); + goto out; + } + + /* We are router and we will perform route lookup for the destination + and send the message to the correct route. */ + if (server->server_type == SILC_ROUTER && !server->standalone) { + + /* If we don't have specific route for the destination we will send + it to our primary route (default route). */ + router = silc_server_route_check(id->ip.s_addr, server->id->port); + if (router) { + dst_sock = (SilcSocketConnection)router->connection; + } else { + router = server->id_entry->router; + dst_sock = (SilcSocketConnection)router->connection; + } + + /* Send packet */ + silc_server_private_message_send_internal(server, dst_sock, + router, packet); + goto out; + } + + err: + silc_server_send_error(server, sock, + "No such nickname: Private message not sent"); + out: + silc_buffer_free(buffer); +} + +SilcChannelList *silc_find_channel(SilcServer server, SilcChannelID *id) +{ + int i; + SilcIDCache *id_cache; + +#define LCC(x) server->local_list->channel_cache[(x)] +#define LCCC(x) server->local_list->channel_cache_count[(x)] + + for (i = 0; i < 96; i++) { + if (LCC(i) == NULL) + continue; + if (silc_idcache_find_by_id(LCC(i), LCCC(i), (void *)id, + SILC_ID_CHANNEL, &id_cache)) + return (SilcChannelList *)id_cache->context; + } + + return NULL; +#undef LCC +#undef LCCC +} + +/* Process received channel message. */ + +void silc_server_channel_message(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcChannelList *channel = NULL; + SilcChannelID *id = NULL; + SilcClientID *sender; + SilcBuffer buffer = packet->buffer; + + SILC_LOG_DEBUG(("Processing channel message")); + + /* Check MAC */ + if (!silc_server_packet_check_mac(server, sock, buffer)) + goto out; + + /* Sanity checks */ + if (packet->dst_id_type != SILC_ID_CHANNEL) { + SILC_LOG_ERROR(("Received bad message for channel, dropped")); + SILC_LOG_DEBUG(("Received bad message for channel, dropped")); + goto out; + } + + /* Send to local clients */ + id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL); + channel = silc_find_channel(server, id); + if (!channel) { + SILC_LOG_DEBUG(("Could not find channel")); + goto out; + } + + /* Distribute the packet to our local clients. This will send the + packet for further routing as well, if needed. */ + sender = silc_id_str2id(packet->src_id, packet->src_id_type); + silc_server_packet_relay_to_channel(server, sock, channel, sender, + packet->src_id_type, + packet->buffer->data, + packet->buffer->len, FALSE); + + out: + silc_buffer_free(buffer); +} + +/* Received channel key packet. We distribute the key to all of our locally + connected clients on the channel. Router ignores the packet. */ + +void silc_server_channel_key(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcChannelKeyPayload payload = NULL; + SilcChannelID *id = NULL; + SilcChannelList *channel; + SilcClientList *client; + unsigned char *key; + unsigned int key_len; + char *cipher; + int i; + + if (server->server_type == SILC_ROUTER) + goto out; + + if (packet->src_id_type != SILC_ID_SERVER && + sock->type != SILC_SOCKET_TYPE_ROUTER) + goto out; + + /* Decode channel key payload */ + payload = silc_channel_key_parse_payload(buffer); + if (!payload) { + SILC_LOG_ERROR(("Bad channel key payload, dropped")); + SILC_LOG_DEBUG(("Bad channel key payload, dropped")); + } + + /* Get channel ID */ + id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL); + if (!id) + goto out; + + /* Get the channel entry */ + channel = silc_idlist_find_channel_by_id(server->local_list->channels, id); + if (!channel) { + SILC_LOG_ERROR(("Received key for non-existent channel")); + SILC_LOG_DEBUG(("Received key for non-existent channel")); + goto out; + } + + /* Save the key for us as well */ + key = silc_channel_key_get_key(payload, &key_len); + if (!key) + goto out; + cipher = silc_channel_key_get_cipher(payload, NULL);; + if (!cipher) + goto out; + channel->key_len = key_len; + channel->key = silc_calloc(key_len, sizeof(unsigned char)); + memcpy(channel->key, key, key_len); + silc_cipher_alloc(cipher, &channel->channel_key); + channel->channel_key->cipher->set_key(channel->channel_key->context, + key, key_len); + + /* Distribute the key to all clients on the channel */ + for (i = 0; i < channel->user_list_count; i++) { + client = channel->user_list[i].client; + + if (client) + silc_server_packet_send_dest(server, client->connection, + SILC_PACKET_CHANNEL_KEY, 0, + client->id, SILC_ID_CLIENT, + buffer->data, buffer->len, FALSE); + } + + out: + if (id) + silc_free(id); + if (payload) + silc_channel_key_free_payload(payload); + silc_buffer_free(buffer); +} + +/* Sends error message. Error messages may or may not have any + implications. */ + +void silc_server_send_error(SilcServer server, + SilcSocketConnection sock, + const char *fmt, ...) +{ + va_list ap; + unsigned char buf[4096]; + + memset(buf, 0, sizeof(buf)); + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, + buf, strlen(buf), FALSE); +} + +/* Sends notify message */ + +void silc_server_send_notify(SilcServer server, + SilcSocketConnection sock, + const char *fmt, ...) +{ + va_list ap; + unsigned char buf[4096]; + + memset(buf, 0, sizeof(buf)); + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, + buf, strlen(buf), FALSE); +} + +/* Sends notify message to a channel. The notify message sent is + distributed to all clients on the channel. */ + +void silc_server_send_notify_to_channel(SilcServer server, + SilcChannelList *channel, + const char *fmt, ...) +{ + va_list ap; + unsigned char buf[4096]; + + memset(buf, 0, sizeof(buf)); + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + silc_server_packet_send_to_channel(server, channel, buf, + strlen(buf), FALSE); +} + +/* Sends New ID Payload to remote end. The packet is used to distribute + information about new registered clients, servers, channel etc. usually + to routers so that they can keep these information up to date. + If the argument `broadcast' is TRUE then the packet is sent as + broadcast packet. */ + +void silc_server_send_new_id(SilcServer server, + SilcSocketConnection sock, + int broadcast, + void *id, SilcIdType id_type, + unsigned int id_len) +{ + SilcBuffer packet; + unsigned char *id_string; + + id_string = silc_id_id2str(id, id_type); + if (!id_string) + return; + + packet = silc_buffer_alloc(2 + 2 + id_len); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(id_type), + SILC_STR_UI_SHORT(id_len), + SILC_STR_UI_XNSTRING(id_string, id_len), + SILC_STR_END); + + silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, + broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, + packet->data, packet->len, FALSE); + silc_free(id_string); + silc_buffer_free(packet); +} + +/* Sends Replace ID payload to remote end. This is used to replace old + ID with new ID sent in the packet. This is called for example when + user changes nickname and we create new ID for the user. If the + argument `broadcast' is TRUE then the packet is sent as + broadcast packet. */ +/* XXX It would be expected that the new id is same type as the old + ID. :) */ + +void silc_server_send_replace_id(SilcServer server, + SilcSocketConnection sock, + int broadcast, + void *old_id, SilcIdType old_id_type, + unsigned int old_id_len, + void *new_id, SilcIdType new_id_type, + unsigned int new_id_len) +{ + SilcBuffer packet; + unsigned char *oid; + unsigned char *nid; + + oid = silc_id_id2str(old_id, old_id_type); + if (!oid) + return; + + nid = silc_id_id2str(new_id, new_id_type); + if (!nid) + return; + + packet = silc_buffer_alloc(2 + 2 + 2 + 2 + old_id_len + new_id_len); + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(old_id_type), + SILC_STR_UI_SHORT(old_id_len), + SILC_STR_UI_XNSTRING(oid, old_id_len), + SILC_STR_UI_SHORT(new_id_type), + SILC_STR_UI_SHORT(new_id_len), + SILC_STR_UI_XNSTRING(nid, new_id_len), + SILC_STR_END); + + silc_server_packet_send(server, sock, SILC_PACKET_REPLACE_ID, + broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, + packet->data, packet->len, FALSE); + silc_free(oid); + silc_free(nid); + silc_buffer_free(packet); +} + +/* Creates new channel. */ + +SilcChannelList *silc_server_new_channel(SilcServer server, + SilcServerID *router_id, + char *cipher, char *channel_name) +{ + int i, channel_len; + SilcChannelID *channel_id; + SilcChannelList *entry; + SilcCipher key; + unsigned char channel_key[32], *id_string; + SilcBuffer packet; + + SILC_LOG_DEBUG(("Creating new channel")); + +#define LCC(x) server->local_list->channel_cache[(x) - 32] +#define LCCC(x) server->local_list->channel_cache_count[(x) - 32] + + /* Create channel key */ + for (i = 0; i < 32; i++) + channel_key[i] = silc_rng_get_byte(server->rng); + + if (!cipher) + cipher = "twofish"; + + /* Allocate keys */ + silc_cipher_alloc(cipher, &key); + key->cipher->set_key(key->context, channel_key, 16); + + /* Create the channel */ + silc_id_create_channel_id(router_id, server->rng, &channel_id); + silc_idlist_add_channel(&server->local_list->channels, channel_name, + SILC_CHANNEL_MODE_NONE, channel_id, NULL, /*XXX*/ + key, &entry); + LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]), + LCCC(channel_name[0]), + channel_name, SILC_ID_CHANNEL, + channel_id, (void *)entry); + entry->key = silc_calloc(16, sizeof(*entry->key)); + entry->key_len = 16; + memcpy(entry->key, channel_key, 16); + memset(channel_key, 0, sizeof(channel_key)); + + /* Notify other routers about the new channel. We send the packet + to our primary route. */ + if (server->standalone == FALSE) { + channel_len = strlen(channel_name); + id_string = silc_id_id2str(entry->id, SILC_ID_CHANNEL); + packet = silc_buffer_alloc(2 + SILC_ID_CHANNEL_LEN); + + silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet)); + silc_buffer_format(packet, + SILC_STR_UI_SHORT(channel_len), + SILC_STR_UI_XNSTRING(channel_name, channel_len), + SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN), + SILC_STR_UI_XNSTRING(id_string, SILC_ID_CHANNEL_LEN), + SILC_STR_END); + + /* Send the packet to our router. */ + silc_server_packet_send(server, (SilcSocketConnection) + server->id_entry->router->connection, + SILC_PACKET_NEW_CHANNEL_USER, 0, + packet->data, packet->len, TRUE); + + silc_free(id_string); + silc_buffer_free(packet); + } + +#undef LCC +#undef LCCC + return entry; +} + +/* Create new client. This processes incoming NEW_CLIENT packet and creates + Client ID for the client and adds it to lists and cache. */ + +SilcClientList *silc_server_new_client(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcClientList *id_entry; + char *username = NULL, *realname = NULL, *id_string; + SilcBuffer reply; + + SILC_LOG_DEBUG(("Creating new client")); + + if (sock->type != SILC_SOCKET_TYPE_CLIENT) + return NULL; + +#define LCC(x) server->local_list->client_cache[(x) - 32] +#define LCCC(x) server->local_list->client_cache_count[(x) - 32] + + silc_buffer_unformat(buffer, + SILC_STR_UI16_STRING_ALLOC(&username), + SILC_STR_UI16_STRING_ALLOC(&realname), + SILC_STR_END); + + /* Set the pointers to the client list and create new client ID */ + id_entry = (SilcClientList *)sock->user_data; + id_entry->nickname = strdup(username); + id_entry->username = username; + id_entry->userinfo = realname; + silc_id_create_client_id(server->id, server->rng, server->md5hash, + username, &id_entry->id); + + /* Add to client cache */ + LCCC(username[0]) = silc_idcache_add(&LCC(username[0]), + LCCC(username[0]), + username, SILC_ID_CLIENT, + id_entry->id, (void *)id_entry); + + /* Notify our router about new client on the SILC network */ + if (!server->standalone) + silc_server_send_new_id(server, (SilcSocketConnection) + server->id_entry->router->connection, + server->server_type == SILC_SERVER ? TRUE : FALSE, + id_entry->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN); + + /* Send the new client ID to the client. */ + id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT); + reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN); + silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply)); + silc_buffer_format(reply, + SILC_STR_UI_SHORT(SILC_ID_CLIENT), + SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN), + SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN), + SILC_STR_END); + silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, + reply->data, reply->len, FALSE); + silc_free(id_string); + silc_buffer_free(reply); + + /* Send some nice info to the client */ + silc_server_send_notify(server, sock, + "Welcome to the SILC Network %s@%s", + username, + sock->hostname ? sock->hostname : sock->ip); + silc_server_send_notify(server, sock, + "Your host is %s, running version %s", + server->config->server_info->server_name, + server_version); + silc_server_send_notify(server, sock, + "Your connection is secured with %s cipher, " + "key length %d bits", + id_entry->send_key->cipher->name, + id_entry->send_key->cipher->key_len); + silc_server_send_notify(server, sock, + "Your current nickname is %s", + id_entry->nickname); + + /* XXX Send motd */ + +#undef LCC +#undef LCCC + return id_entry; +} + +/* Create new server. This processes incoming NEW_SERVER packet and + saves the received Server ID. The server is our locally connected + server thus we save all the information and save it to local list. + This funtion can be used by both normal server and router server. + If normal server uses this it means that its router has connected + to the server. If router uses this it means that one of the cell's + servers is connected to the router. */ + +SilcServerList *silc_server_new_server(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcServerList *id_entry; + unsigned char *server_name, *id_string; + + SILC_LOG_DEBUG(("Creating new server")); + + if (sock->type != SILC_SOCKET_TYPE_SERVER && + sock->type != SILC_SOCKET_TYPE_ROUTER) + return NULL; + +#define LSC(x) server->local_list->server_cache[(x) - 32] +#define LSCC(x) server->local_list->server_cache_count[(x) - 32] + + silc_buffer_unformat(buffer, + SILC_STR_UI16_STRING_ALLOC(&id_string), + SILC_STR_UI16_STRING_ALLOC(&server_name), + SILC_STR_END); + + /* Save ID and name */ + id_entry = (SilcServerList *)sock->user_data; + id_entry->id = silc_id_str2id(id_string, SILC_ID_SERVER); + id_entry->server_name = server_name; + + /* Add to server cache */ + LSCC(server_name[0]) = + silc_idcache_add(&LSC(server_name[0]), + LSCC(server_name[0]), + server_name, SILC_ID_SERVER, + id_entry->id, (void *)id_entry); + + /* Distribute the information about new server in the SILC network + to our router. If we are normal server we won't send anything + since this connection must be our router connection. */ + if (server->server_type == SILC_ROUTER && !server->standalone) + silc_server_send_new_id(server, server->id_entry->router->connection, + TRUE, id_entry->id, SILC_ID_SERVER, + SILC_ID_SERVER_LEN); + + silc_free(id_string); + +#undef LSC +#undef LSCC + return id_entry; +} + +/* Processes incoming New ID Payload. New ID Payload is used to distribute + information about newly registered clients, servers and created + channels. */ + +void silc_server_new_id(SilcServer server, SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcBuffer buffer = packet->buffer; + SilcIdType id_type; + unsigned char *id_string; + void *id; + + SILC_LOG_DEBUG(("Processing new ID")); + + if (sock->type == SILC_SOCKET_TYPE_CLIENT || + server->server_type == SILC_SERVER) + return; + + silc_buffer_unformat(buffer, + SILC_STR_UI_SHORT(&id_type), + SILC_STR_UI16_STRING_ALLOC(&id_string), + SILC_STR_END); + + /* Normal server cannot have other normal server connections */ + if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER) + goto out; + + id = silc_id_str2id(id_string, id_type); + if (!id) + goto out; + + /* XXX Do check whether the packet is coming outside the cell or + from someone inside the cell. If outside use global lists otherwise + local lists. */ + /* XXX If using local list set the idlist->connection to the sender's + socket connection as it is used in packet sending */ + + switch(id_type) { + case SILC_ID_CLIENT: + { + SilcClientList *idlist; + + /* Add the client to our local list. We are router and we keep + cell specific local database of all clients in the cell. */ + silc_idlist_add_client(&server->local_list->clients, NULL, NULL, NULL, + id, sock->user_data, NULL, NULL, + NULL, NULL, &idlist); + idlist->connection = sock; + } + break; + + case SILC_ID_SERVER: + { + SilcServerList *idlist; + + /* Add the server to our local list. We are router and we keep + cell specific local database of all servers in the cell. */ + silc_idlist_add_server(&server->local_list->servers, NULL, 0, + id, server->id_entry, NULL, NULL, + NULL, NULL, &idlist); + idlist->connection = sock; + } + break; + + case SILC_ID_CHANNEL: + /* Add the channel to our local list. We are router and we keep + cell specific local database of all channels in the cell. */ + silc_idlist_add_channel(&server->local_list->channels, NULL, 0, + id, server->id_entry, NULL, NULL); + break; + + default: + goto out; + break; + } + + out: + silc_free(id_string); +} diff --git a/apps/silcd/server.h b/apps/silcd/server.h new file mode 100644 index 00000000..ebe661c9 --- /dev/null +++ b/apps/silcd/server.h @@ -0,0 +1,130 @@ +/* + + server.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef SERVER_H +#define SERVER_H + +/* Forward declaration for SILC Server object. The actual object is + defined in internal header file for server routines. I want to keep + the object private hence this declaration. */ +typedef struct SilcServerObjectStruct *SilcServer; + +#define SILC_SERVER_MAX_CONNECTIONS 10000 + +/* General definitions */ + +#define SILC_SERVER 0 +#define SILC_ROUTER 1 + +/* Prototypes */ +int silc_server_alloc(SilcServer *new_server); +void silc_server_free(SilcServer server); +int silc_server_init(SilcServer server); +void silc_server_run(SilcServer server); +void silc_server_stop(SilcServer server); +void silc_server_packet_send(SilcServer server, + SilcSocketConnection sock, + SilcPacketType type, + SilcPacketFlags flags, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_server_packet_send_dest(SilcServer server, + SilcSocketConnection sock, + SilcPacketType type, + SilcPacketFlags flags, + void *dst_id, + SilcIdType dst_id_type, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_server_packet_forward(SilcServer server, + SilcSocketConnection sock, + unsigned char *data, unsigned int data_len, + int force_send); +void silc_server_packet_send_to_channel(SilcServer server, + SilcChannelList *channel, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_server_packet_relay_to_channel(SilcServer server, + SilcSocketConnection sender_sock, + SilcChannelList *channel, + void *sender, + SilcIdType sender_type, + unsigned char *data, + unsigned int data_len, + int force_send); +void silc_server_packet_relay_command_reply(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_server_close_connection(SilcServer server, + SilcSocketConnection sock); +void silc_server_free_sock_user_data(SilcServer server, + SilcSocketConnection sock); +void silc_server_remove_from_channels(SilcServer server, + SilcSocketConnection sock, + SilcClientList *client); +void silc_server_disconnect_remote(SilcServer server, + SilcSocketConnection sock, + const char *fmt, ...); +void silc_server_private_message(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_server_channel_message(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_server_channel_key(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_server_send_error(SilcServer server, + SilcSocketConnection sock, + const char *fmt, ...); +void silc_server_send_notify(SilcServer server, + SilcSocketConnection sock, + const char *fmt, ...); +void silc_server_send_notify_to_channel(SilcServer server, + SilcChannelList *channel, + const char *fmt, ...); +void silc_server_send_new_id(SilcServer server, + SilcSocketConnection sock, + int broadcast, + void *id, SilcIdType id_type, + unsigned int id_len); +void silc_server_send_replace_id(SilcServer server, + SilcSocketConnection sock, + int broadcast, + void *old_id, SilcIdType old_id_type, + unsigned int old_id_len, + void *new_id, SilcIdType new_id_type, + unsigned int new_id_len); +SilcChannelList *silc_server_new_channel(SilcServer server, + SilcServerID *router_id, + char *cipher, char *channel_name); +SilcClientList *silc_server_new_client(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +SilcServerList *silc_server_new_server(SilcServer server, + SilcSocketConnection sock, + SilcPacketContext *packet); +void silc_server_new_id(SilcServer server, SilcSocketConnection sock, + SilcPacketContext *packet); + +#endif diff --git a/apps/silcd/server_internal.h b/apps/silcd/server_internal.h new file mode 100644 index 00000000..3ed7cbeb --- /dev/null +++ b/apps/silcd/server_internal.h @@ -0,0 +1,117 @@ +/* + + server_internal.h + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef SERVER_INTERNAL_H +#define SERVER_INTERNAL_H + +/* Server statistics structure. This holds various statistics about + various things. */ +/* XXX TODO */ +typedef struct { + +} SilcServerStatistics; + +/* + SILC Server Object. + +*/ +typedef struct SilcServerObjectStruct { + char *server_name; + int server_type; + int sock; + int standalone; + int listenning; + SilcServerID *id; + SilcIdType id_type; + SilcServerList *id_entry; + + /* SILC server task queues */ + SilcTaskQueue io_queue; + SilcTaskQueue timeout_queue; + SilcTaskQueue generic_queue; + + /* ID lists. */ + SilcIDList local_list; + SilcIDList global_list; + + /* Table of connected sockets */ + SilcSocketConnection *sockets; + + /* Server keys */ + SilcCipher send_key; + SilcCipher receive_key; + SilcCipher none_cipher; + + /* Server public key */ + SilcPKCS public_key; + + /* Hash objects for general hashing */ + SilcHash md5hash; + SilcHash sha1hash; + + /* HMAC objects for MAC's. */ + SilcHmac md5hmac; + SilcHmac sha1hmac; + + /* Configuration object */ + SilcConfigServer config; + + /* Random pool */ + SilcRng rng; + + /* Server statistics */ + SilcServerStatistics stats; + +#ifdef SILC_SIM + /* SIM (SILC Module) table */ + SilcSimContext **sim; + unsigned int sim_count; +#endif +} SilcServerObject; + +/* Macros */ + +/* Registers generic task for file descriptor for reading from network and + writing to network. As being generic task the actual task is allocated + only once and after that the same task applies to all registered fd's. */ +#define SILC_REGISTER_CONNECTION_FOR_IO(fd) \ +do { \ + SilcTask tmptask = silc_task_register(server->generic_queue, (fd), \ + silc_server_packet_process, \ + context, 0, 0, \ + SILC_TASK_GENERIC, \ + SILC_TASK_PRI_NORMAL); \ + silc_task_set_iotype(tmptask, SILC_TASK_WRITE); \ +} while(0) + +#define SILC_SET_CONNECTION_FOR_INPUT(fd) \ +do { \ + silc_schedule_set_listen_fd((fd), (1L << SILC_TASK_READ)); \ +} while(0) + +#define SILC_SET_CONNECTION_FOR_OUTPUT(fd) \ +do { \ + silc_schedule_set_listen_fd((fd), ((1L << SILC_TASK_READ) | \ + (1L << SILC_TASK_WRITE))); \ +} while(0) + +/* Prototypes */ + +#endif diff --git a/apps/silcd/server_version.c b/apps/silcd/server_version.c new file mode 100644 index 00000000..aa7099da --- /dev/null +++ b/apps/silcd/server_version.c @@ -0,0 +1,31 @@ +/* + + server_version.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" + +const char server_version[] = "27062000"; diff --git a/apps/silcd/serverconfig.c b/apps/silcd/serverconfig.c new file mode 100644 index 00000000..326c1336 --- /dev/null +++ b/apps/silcd/serverconfig.c @@ -0,0 +1,1446 @@ +/* + + serverconfig.c + + Author: Pekka Riikonen + + Copyright (C) 1997 - 2000 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ +/* + * $Id$ + * $Log$ + * Revision 1.1 2000/06/27 11:36:56 priikone + * Initial revision + * + * + */ + +#include "serverincludes.h" +#include "server_internal.h" + +/* XXX + All possible configuration sections for SILC server. + + + + Format: + + +: + + + + Format: + + +: + + + + Format: + + +: + + + + This section is used to set the server informations. + + Format: + + +::: + + + + This section is used to set the server's administrative information. + + Format: + + +::: + + + + This section is used to set ports the server is listenning. + + Format: + + +:: + + + + This section is used to set various logging files, their paths + and maximum sizes. All the other directives except those defined + below are ignored in this section. Log files are purged after they + reach the maximum set byte size. + + Format: + + +infologfile:: + +errorlogfile:: + + + + This section is used to define connection classes. These can be + used to optimize the server and the connections. + + Format: + + +::: + + + + This section is used to define client authentications. + + Format: + + +:::: + + + + This section is used to define the server's administration + authentications. + + Format: + + +:::: + + + + This section is used to define the server connections to this + server/router. Only routers can have normal server connections. + Normal servers leave this section epmty. The remote server cannot be + older than specified Version ID. + + Format: + + +::::: + + + + This section is used to define the router connections to this + server/router. Both normal server and router can have router + connections. Normal server usually has only one connection while + a router can have multiple. The remote server cannot be older than + specified Version ID. + + Format: + + +::::: + + + + This section is used to deny specific connections to your server. This + can be used to deny both clients and servers. + + Format: + + +: