Explorar o código

Initial revision

svn:r2
Roger Dingledine %!s(int64=22) %!d(string=hai) anos
pai
achega
9a928eeb12
Modificáronse 67 ficheiros con 12287 adicións e 0 borrados
  1. 124 0
      TODO
  2. 228 0
      src/common/Makefile
  3. 355 0
      src/common/cell.c
  4. 95 0
      src/common/cell.h
  5. 347 0
      src/common/config.c
  6. 88 0
      src/common/config.h
  7. 89 0
      src/common/key.c
  8. 32 0
      src/common/key.h
  9. 74 0
      src/common/log.c
  10. 40 0
      src/common/log.h
  11. 633 0
      src/common/onion.c
  12. 156 0
      src/common/onion.h
  13. 77 0
      src/common/opcell.c
  14. 44 0
      src/common/opcell.h
  15. 39 0
      src/common/policies.h
  16. 147 0
      src/common/routent.c
  17. 189 0
      src/common/routent.h
  18. 260 0
      src/common/scheduler.c
  19. 57 0
      src/common/scheduler.h
  20. 63 0
      src/common/ss.h
  21. 150 0
      src/common/utils.c
  22. 53 0
      src/common/utils.h
  23. 28 0
      src/common/version.h
  24. 99 0
      src/httpap/Makefile
  25. 194 0
      src/httpap/http.c
  26. 46 0
      src/httpap/http.h
  27. 702 0
      src/httpap/httpap.c
  28. 33 0
      src/httpap/httpap.h
  29. 285 0
      src/op/Makefile
  30. 121 0
      src/op/args.c
  31. 41 0
      src/op/args.h
  32. 85 0
      src/op/auth.c
  33. 23 0
      src/op/auth.h
  34. 146 0
      src/op/buffers.c
  35. 25 0
      src/op/buffers.h
  36. 49 0
      src/op/config.c
  37. 30 0
      src/op/config.h
  38. 104 0
      src/op/crypto.c
  39. 23 0
      src/op/crypto.h
  40. 916 0
      src/op/op.c
  41. 54 0
      src/op/op.h
  42. 364 0
      src/op/routers.c
  43. 66 0
      src/op/routers.h
  44. 194 0
      src/op/ss.c
  45. 22 0
      src/op/ss.h
  46. 18 0
      src/or/Makefile
  47. 99 0
      src/or/args.c
  48. 174 0
      src/or/buffers.c
  49. 23 0
      src/or/cell.c
  50. 319 0
      src/or/circuit.c
  51. 212 0
      src/or/command.c
  52. 49 0
      src/or/config.c
  53. 362 0
      src/or/connection.c
  54. 212 0
      src/or/connection_app.c
  55. 130 0
      src/or/connection_op.c
  56. 580 0
      src/or/connection_or.c
  57. 406 0
      src/or/main.c
  58. 62 0
      src/or/onion.c
  59. 411 0
      src/or/or.h
  60. 365 0
      src/or/routers.c
  61. 58 0
      src/orkeygen/Makefile
  62. 119 0
      src/orkeygen/orkeygen.c
  63. 80 0
      src/smtpap/Makefile
  64. 133 0
      src/smtpap/io.c
  65. 2 0
      src/smtpap/io.h
  66. 1393 0
      src/smtpap/smtpap.c
  67. 90 0
      src/smtpap/smtpap.h

+ 124 - 0
TODO

@@ -0,0 +1,124 @@
+
+Obvious things I'd like to do that won't break anything:
+
+* Abstract out crypto calls, with the eventual goal of moving
+  from openssl to something with a more flexible license.
+
+* Test suite. We need one.
+
+* Switch the "return -1" cases that really mean "you've got a bug"
+  into calls to assert().
+
+* Since my OR can handle multiple circuits through a given OP,
+  I think it's clear that the OP should pass new create cells through the
+  same channel. Thus we can take advantage of the padding we're already
+  getting. Does that mean the choose_onion functions should be changed
+  to always pick a favorite OR first, so the OP can minimize the number
+  of outgoing connections it must sustain?
+
+* Rewrite the OP to be non-blocking single-process.
+
+* Add autoconf support.
+  Figure out what .h files we're actually using, and how portable
+  those are.
+
+* Since we're using a stream cipher, an adversary's cell arriving with the
+  same aci will forever trash our circuit. Since each side picks half
+  the aci, for each cell the adversary has a 1/256 chance of trashing a
+  circuit. This is really nasty. We want to make ACIs something reasonably
+  hard to collide with, such as 20 bytes.
+
+  While we're at it, I'd like more than 4 bits for Version. :)
+
+* Exit policies. Since we don't really know what protocol is being spoken,
+  it really comes down to an IP range and port range that we
+  allow/disallow. The 'application' connection can evaluate it and make
+  a decision.
+
+* We currently block on gethostbyname in OR. This is poor. The complex
+  solution is to have a separate process that we talk to. There are some
+  free software versions we can use, but they'll still be tricky. The
+  better answer is to realize that the OP can do the resolution and
+  simply hand the OR an IP directly.
+  A) This prevents us from doing sneaky things like having the name resolve
+     differently at the OR than at the OP. I'm ok with that.
+  B) It actually just shunts the "dns lookups block" problem back onto the
+     OP. But that's ok too, because the OP doesn't have to be as robust.
+     (Heck, can we have the application proxy resolve it, even?)
+
+* I'd like a cleaner interface for the configuration files, keys, etc.
+  Perhaps the next step is a central repository where we download router
+  lists? Something that takes the human more out of the loop.
+
+  We should look into a 'topology communication protocol'; there's one
+  mentioned in the spec that Paul has, but I haven't looked at it to
+  know how complete it is or how well it would work. This would also
+  allow us to add new ORs on the fly. Directory servers, a la the ones
+  we're developing for Mixminion (see http://mixminion.net/), are also
+  a very nice approach to consider.
+
+* Should ORs rotate their link keys periodically?
+
+* We probably want OAEP padding for RSA.
+
+* The parts of the code that say 'FIXME'
+
+Non-obvious things I'd like to do:
+
+(Many of these topics are inter-related. It's clear that we need more
+analysis before we can guess which approaches are good.)
+
+* Padding between ORs, and correct padding between OPs. Currently
+  the OP seems to send padding at a steady rate, but data cells can
+  come more quickly than that. This doesn't provide much protection
+  at all. I'd like to investigate a synchronous mixing approach, where
+  cells are sent at fixed intervals. We need to investigate the effects
+  of this on DoS resistance -- what do we do when we have too many
+  packets? One approach is to do traffic shaping rather than traffic
+  padding -- we gain a bit more resistance to DoS at the expense of some
+  anonymity. Can we compare this analysis to that of the Cottrell Mix,
+  and learn something new? We'll need to decide on exactly how the
+  traffic shaping algorithm works.
+
+* Make the connection buf's grow dynamically as needed. This won't
+  really solve the fundamental problem above, though, that a buffer
+  can be given an adversary-controlled number of cells.
+
+* I'd like to add a scheduler of some sort. Currently we only need one
+  for sending out padding cells, and if these events are periodic and
+  synchronized, we don't yet need a scheduler per se, but rather we just
+  need to have poll return every so often and avoid sending cells onto
+  the sockets except at the appointed time. We're nearly ready to do
+  that as it is, with the separation of write_to_buf() and flush_buf().
+
+  Edge case: what do we do with circuits that receive a destroy
+  cell before all data has been sent out? Currently there's only one
+  (outgoing) buffer per connection, so since it's crypted, a circuit
+  can't recognize its own packet once it's been queued. We could mark
+  the circuits for destruction, and go through and cull them once the
+  buffer is entirely flushed; but with the synchronous approach above,
+  the buffer may never become empty. Perhaps I should implement a callback
+  system, so a function can get called when a particular cell gets sent
+  out. That sounds very flexible, but might also be overkill.
+
+* Currently when a connection goes down, it generates a destroy cell
+  (either in both directions or just the appropriate one). When a
+  destroy cell arrives to an OR (and it gets read after all previous
+  cells have arrived), it delivers a destroy cell for the "other side"
+  of the circuit: if the other side is an OP or APP, it closes the entire
+  connection as well.
+
+  But by "a connection going down", I mean "I read eof from it". Yet
+  reading an eof simply means that it promises not to send any more
+  data. It may still be perfectly fine receiving data (read "man 2
+  shutdown"). In fact, some webservers work that way -- the client sends
+  his entire request, and when the webserver reads an eof it begins
+  its response. We currently don't support that sort of protocol; we
+  may want to switch to some sort of a two-way-destry-ripple technique
+  (where a destroy makes its way all the way to the end of the circuit
+  before being echoed back, and data stops flowing only when a destroy
+  has been received from both sides of the circuit); this extends the
+  one-hop-ack approach that Matej used.
+
+* Reply onions. Hrm.
+

+ 228 - 0
src/common/Makefile

@@ -0,0 +1,228 @@
+SRC=log.c config.c utils.c key.c onion.c cell.c routent.c scheduler.c
+OBJ=${SRC:.c=.o}
+INCLUDE=-I/usr/local/ssl/include
+
+CFLAGS=${INCLUDE} -Wall -Wpointer-arith -O2
+
+all:	${OBJ}
+
+clean:
+	rm -f $(OBJ)
+
+depend:
+	makedepend -- $(CFLAGS) -- $(SRC)
+
+# DO NOT DELETE
+
+
+cell.o: cell.h log.h /usr/include/alloca.h /usr/include/asm/socket.h
+cell.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h
+cell.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+cell.o: /usr/include/bits/environments.h /usr/include/bits/in.h
+cell.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h
+cell.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+cell.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+cell.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+cell.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h
+cell.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+cell.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h
+cell.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+cell.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h
+cell.o: /usr/include/endian.h /usr/include/features.h /usr/include/getopt.h
+cell.o: /usr/include/gnu/stubs.h /usr/include/limits.h
+cell.o: /usr/include/linux/limits.h /usr/include/netinet/in.h
+cell.o: /usr/include/openssl/rand.h /usr/include/stdint.h
+cell.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/sys/cdefs.h
+cell.o: /usr/include/syslog.h /usr/include/sys/select.h
+cell.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+cell.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/unistd.h
+cell.o: /usr/include/xlocale.h
+cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h
+config.o: config.h log.h /usr/include/alloca.h /usr/include/asm/errno.h
+config.o: /usr/include/bits/endian.h /usr/include/bits/errno.h
+config.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+config.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+config.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+config.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h
+config.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+config.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h
+config.o: /usr/include/features.h /usr/include/_G_config.h
+config.o: /usr/include/gconv.h /usr/include/gnu/stubs.h /usr/include/libio.h
+config.o: /usr/include/linux/errno.h /usr/include/stdio.h
+config.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/sys/cdefs.h
+config.o: /usr/include/syslog.h /usr/include/sys/select.h
+config.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+config.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/wchar.h
+config.o: /usr/include/xlocale.h
+config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+key.o: config.h key.h log.h /usr/include/alloca.h /usr/include/asm/errno.h
+key.o: /usr/include/bits/endian.h /usr/include/bits/errno.h
+key.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+key.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+key.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+key.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h
+key.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+key.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h
+key.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/gnu/stubs.h
+key.o: /usr/include/libio.h /usr/include/linux/errno.h
+key.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h
+key.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h
+key.o: /usr/include/openssl/buffer.h /usr/include/openssl/cast.h
+key.o: /usr/include/openssl/crypto.h /usr/include/openssl/des.h
+key.o: /usr/include/openssl/dh.h /usr/include/openssl/dsa.h
+key.o: /usr/include/openssl/e_os2.h /usr/include/openssl/err.h
+key.o: /usr/include/openssl/evp.h /usr/include/openssl/lhash.h
+key.o: /usr/include/openssl/md2.h /usr/include/openssl/md4.h
+key.o: /usr/include/openssl/md5.h /usr/include/openssl/objects.h
+key.o: /usr/include/openssl/obj_mac.h /usr/include/openssl/opensslconf.h
+key.o: /usr/include/openssl/opensslv.h /usr/include/openssl/pem2.h
+key.o: /usr/include/openssl/pem.h /usr/include/openssl/pkcs7.h
+key.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h
+key.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h
+key.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h
+key.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h
+key.o: /usr/include/openssl/x509.h /usr/include/openssl/x509_vfy.h
+key.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+key.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+key.o: /usr/include/sys/select.h /usr/include/sys/syslog.h
+key.o: /usr/include/sys/sysmacros.h /usr/include/sys/types.h
+key.o: /usr/include/time.h /usr/include/wchar.h /usr/include/xlocale.h
+key.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+key.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+log.o: log.h /usr/include/asm/errno.h /usr/include/bits/errno.h
+log.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+log.o: /usr/include/bits/stdio_lim.h /usr/include/bits/types.h
+log.o: /usr/include/bits/wchar.h /usr/include/errno.h /usr/include/features.h
+log.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/gnu/stubs.h
+log.o: /usr/include/libio.h /usr/include/linux/errno.h /usr/include/stdio.h
+log.o: /usr/include/string.h /usr/include/sys/cdefs.h /usr/include/syslog.h
+log.o: /usr/include/sys/syslog.h /usr/include/wchar.h /usr/include/xlocale.h
+log.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+log.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+onion.o: cell.h log.h onion.h routent.h /usr/include/alloca.h
+onion.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h
+onion.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h
+onion.o: /usr/include/bits/byteswap.h /usr/include/bits/confname.h
+onion.o: /usr/include/bits/endian.h /usr/include/bits/environments.h
+onion.o: /usr/include/bits/errno.h /usr/include/bits/in.h
+onion.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h
+onion.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+onion.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+onion.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+onion.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h
+onion.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+onion.o: /usr/include/bits/types.h /usr/include/bits/uio.h
+onion.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+onion.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+onion.o: /usr/include/bits/xopen_lim.h /usr/include/endian.h
+onion.o: /usr/include/errno.h /usr/include/features.h /usr/include/_G_config.h
+onion.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h
+onion.o: /usr/include/libio.h /usr/include/limits.h /usr/include/linux/errno.h
+onion.o: /usr/include/linux/limits.h /usr/include/netinet/in.h
+onion.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h
+onion.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h
+onion.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h
+onion.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h
+onion.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h
+onion.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h
+onion.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h
+onion.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h
+onion.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h
+onion.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+onion.o: /usr/include/openssl/rand.h /usr/include/openssl/rc2.h
+onion.o: /usr/include/openssl/rc4.h /usr/include/openssl/ripemd.h
+onion.o: /usr/include/openssl/rsa.h /usr/include/openssl/safestack.h
+onion.o: /usr/include/openssl/sha.h /usr/include/openssl/stack.h
+onion.o: /usr/include/openssl/symhacks.h /usr/include/stdint.h
+onion.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+onion.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+onion.o: /usr/include/sys/select.h /usr/include/sys/socket.h
+onion.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+onion.o: /usr/include/sys/timeb.h /usr/include/sys/time.h
+onion.o: /usr/include/sys/types.h /usr/include/sys/uio.h /usr/include/time.h
+onion.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h
+onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h version.h
+routent.o: cell.h routent.h /usr/include/alloca.h /usr/include/bits/confname.h
+routent.o: /usr/include/bits/endian.h /usr/include/bits/environments.h
+routent.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h
+routent.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+routent.o: /usr/include/bits/sigset.h /usr/include/bits/stdio_lim.h
+routent.o: /usr/include/bits/time.h /usr/include/bits/types.h
+routent.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+routent.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+routent.o: /usr/include/endian.h /usr/include/features.h
+routent.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h
+routent.o: /usr/include/gnu/stubs.h /usr/include/libio.h
+routent.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h
+routent.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h
+routent.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h
+routent.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h
+routent.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h
+routent.o: /usr/include/openssl/evp.h /usr/include/openssl/md2.h
+routent.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h
+routent.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h
+routent.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+routent.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h
+routent.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h
+routent.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h
+routent.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h
+routent.o: /usr/include/stdint.h /usr/include/stdio.h /usr/include/stdlib.h
+routent.o: /usr/include/sys/cdefs.h /usr/include/sys/select.h
+routent.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h
+routent.o: /usr/include/sys/time.h /usr/include/sys/types.h
+routent.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h
+routent.o: /usr/include/xlocale.h
+routent.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+routent.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+scheduler.o: log.h scheduler.h /usr/include/bits/confname.h
+scheduler.o: /usr/include/bits/environments.h /usr/include/bits/posix_opt.h
+scheduler.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+scheduler.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+scheduler.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+scheduler.o: /usr/include/bits/types.h /usr/include/bits/wchar.h
+scheduler.o: /usr/include/bits/wordsize.h /usr/include/features.h
+scheduler.o: /usr/include/_G_config.h /usr/include/gconv.h
+scheduler.o: /usr/include/getopt.h /usr/include/gnu/stubs.h
+scheduler.o: /usr/include/libio.h /usr/include/malloc.h /usr/include/stdio.h
+scheduler.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+scheduler.o: /usr/include/sys/select.h /usr/include/sys/syslog.h
+scheduler.o: /usr/include/sys/time.h /usr/include/time.h /usr/include/unistd.h
+scheduler.o: /usr/include/wchar.h
+scheduler.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+scheduler.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+utils.o: log.h /usr/include/alloca.h /usr/include/asm/errno.h
+utils.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h
+utils.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+utils.o: /usr/include/bits/environments.h /usr/include/bits/errno.h
+utils.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h
+utils.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+utils.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+utils.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+utils.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h
+utils.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+utils.o: /usr/include/bits/types.h /usr/include/bits/uio.h
+utils.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+utils.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+utils.o: /usr/include/bits/xopen_lim.h /usr/include/ctype.h
+utils.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h
+utils.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h
+utils.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/limits.h
+utils.o: /usr/include/linux/errno.h /usr/include/linux/limits.h
+utils.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+utils.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+utils.o: /usr/include/sys/select.h /usr/include/sys/socket.h
+utils.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+utils.o: /usr/include/sys/time.h /usr/include/sys/types.h
+utils.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/time.h
+utils.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h
+utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h utils.h

+ 355 - 0
src/common/cell.c

@@ -0,0 +1,355 @@
+/**
+ * cell.c
+ * Cell manipulation.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.16  2002/06/14 20:41:19  mp292
+ * Parameter checking error - thanks Roger.
+ *
+ * Revision 1.15  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.14  2002/04/02 10:19:37  badbytes
+ * Stricter parameter checking.
+ *
+ * Revision 1.13  2002/03/12 23:30:19  mp292
+ * Removed some memory overruns.
+ *
+ * Revision 1.12  2002/03/03 00:06:45  mp292
+ * Modifications to support re-transmission.
+ *
+ * Revision 1.11  2002/02/03 22:41:45  mp292
+ * Changes to cell size.
+ *
+ * Revision 1.10  2002/01/21 20:57:19  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.9  2002/01/17 15:00:43  mp292
+ * Fixed a bug which caused malloc() generate a seg fault.
+ *
+ * Revision 1.8  2002/01/16 23:01:54  mp292
+ * First phase of system testing completed (main functionality).
+ *
+ * Revision 1.7  2002/01/14 13:05:37  badbytes
+ * System testing in progress.
+ *
+ * Revision 1.6  2002/01/10 13:15:54  badbytes
+ * Fixed ACI size from 32bits to 16bits.
+ *
+ * Revision 1.5  2002/01/07 13:06:06  badbytes
+ * cell.ACI is now cell.aci
+ *
+ * Revision 1.4  2002/01/07 09:26:00  badbytes
+ * Added pack_create() and pack_data().
+ *
+ * Revision 1.3  2002/01/07 07:48:34  badbytes
+ * fixed new_create_cell()
+ *
+ * Revision 1.2  2002/01/04 12:11:54  badbytes
+ * Syntax errors fixed.
+ *
+ * Revision 1.1  2002/01/04 12:08:34  badbytes
+ * Added functions for cell creation.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+  
+#include <openssl/rand.h>
+
+#include "cell.h"
+#include "log.h"
+
+cell_t *new_padding_cell()
+{
+  cell_t *c = NULL;
+  int retval;
+  
+  c = malloc(sizeof(cell_t));
+  if (!c) /* malloc() error */
+    return NULL;
+
+  retval = RAND_pseudo_bytes((unsigned char *)c,sizeof(cell_t));
+  if (retval == -1) /* RAND_pseudo_bytes() error */
+  {
+    free((void *)c);
+    return NULL;
+  } /* RAND_pseudo_bytes() error */
+
+  c->command = CELL_PADDING;
+  
+  return c;
+}
+
+cell_t *new_destroy_cell(uint16_t aci)
+{
+  cell_t *c = NULL;
+  int retval;
+
+  if (aci) /* valid ACI */
+  {
+    c = (cell_t *)malloc(sizeof(cell_t));
+    if (!c) /* malloc error */
+      return NULL;
+    
+    retval = RAND_pseudo_bytes((unsigned char *)c+3,sizeof(cell_t)-3);
+    if (retval == -1) /* RAND_pseudo_bytes() error */
+    {
+      free((void *)c);
+      return NULL;
+    } /* RAND_pseudo_bytes() error */
+    
+    c->aci = aci;
+    c->command = CELL_DESTROY;
+    
+    return c;
+  } /* valid ACI */
+  else /* invalid ACI */
+    return NULL;
+}
+
+cell_t *new_ack_cell(uint16_t aci)
+{
+  cell_t *c = NULL;
+  int retval;
+  
+  if (aci) /* valid ACI */
+  {
+    c = (cell_t *)malloc(sizeof(cell_t));
+    if (!c) /* malloc error */
+      return NULL;
+    
+    retval = RAND_pseudo_bytes((unsigned char *)c+3,sizeof(cell_t)-3);
+    if (retval == -1) /* RAND_pseudo_bytes() error */
+    {
+      free((void *)c);
+      return NULL;
+    } /* RAND_pseudo_bytes() error */
+    
+    c->aci = aci;
+    c->command = CELL_ACK;
+    
+    return c;
+  } /* valid ACI */
+  else /* invalid ACI */
+    return NULL;
+}
+
+cell_t *new_nack_cell(uint16_t aci)
+{
+  cell_t *c = NULL;
+  int retval;
+  
+  if (aci) /* valid ACI */
+  {
+    c = (cell_t *)malloc(sizeof(cell_t));
+    if (!c) /* malloc error */
+      return NULL;
+    
+    retval = RAND_pseudo_bytes((unsigned char *)c+3,sizeof(cell_t)-3);
+    if (retval == -1) /* RAND_pseudo_bytes() error */
+    {
+      free((void *)c);
+      return NULL;
+    } /* RAND_pseudo_bytes() error */
+    
+    c->aci = aci;
+    c->command = CELL_NACK;
+    
+    return c;
+  } /* valid ACI */
+  else /* invalid ACI */
+    return NULL;
+}
+
+cell_t *new_create_cell(uint16_t aci, unsigned char length, unsigned char *buf)
+{
+  cell_t *c = NULL;
+  int retval;
+
+  if ((aci) && (buf) && (length <= CELL_PAYLOAD_SIZE)) /* valid parameters */
+  {
+    c = (cell_t *)malloc(sizeof(cell_t));
+    if (!c) /* malloc() error */
+      return NULL;
+    
+    c->command = CELL_CREATE;
+    c->aci = aci;
+    c->length = length;
+    c->seq = 0;
+    
+    memcpy((void *)c->payload, (void *)buf, length);
+    retval = RAND_pseudo_bytes((unsigned char *)(c->payload+length),CELL_PAYLOAD_SIZE-length);
+    if (retval == -1) /* RAND_pseudo_bytes() error */
+    {
+      free((void *)c);
+      return NULL;
+    } /* RAND_pseudo_bytes() error */
+    
+    return c;
+  } /* valid parameters */
+  else /* invalid parameters */
+    return NULL;
+}
+
+
+cell_t *new_data_cell(uint16_t aci, unsigned char length, unsigned char *buf)
+{
+  cell_t *c = NULL;
+  int retval;
+
+  if ((aci) && (buf) && (length <= CELL_PAYLOAD_SIZE)) /* valid parameters */
+  {
+    c = malloc(sizeof(cell_t));
+    if (!c) /* malloc() error */
+      return NULL;
+    
+    c->command = CELL_DATA;
+    c->aci = aci;
+    c->length = length;
+    c->seq = 0;
+    
+    memcpy((void *)c->payload, (void *)buf, length);
+    retval = RAND_pseudo_bytes((unsigned char *)(c->payload+length),CELL_PAYLOAD_SIZE-length);
+    if (retval == -1) /* RAND_pseudo_bytes() error */
+    {
+      free((void *)c);
+      return NULL;
+    } /* RAND_pseudo_bytes() error */
+    
+    return c;
+  } /* valid parameters */
+  else /* invalid parameters */
+    return NULL;
+}
+
+int pack_create(uint16_t aci, unsigned char *onion, uint32_t onionlen, unsigned char **cellbuf, unsigned int *cellbuflen)
+{
+  cell_t *c;
+  unsigned char *buf;
+  unsigned int buflen;
+  unsigned int cells;
+  unsigned int dataleft;
+  unsigned int i;
+  
+  if ((aci) && (onion) && (cellbuf) && (cellbuflen) && (onionlen)) /* valid parameters */
+  {
+    /* copy the onion into a buffer, prepend with onion length */
+    buflen = onionlen+4;
+    buf = (unsigned char *)malloc(buflen);
+    if (!buf) /* malloc() error */
+      return -1;
+  
+    log(LOG_DEBUG,"pack_create() : Setting onion length to %u.",onionlen);
+    onionlen=htonl(onionlen);
+    memcpy((void *)buf,(void *)&onionlen,4);
+    onionlen=ntohl(onionlen);
+    memcpy((void *)(buf+4),(void *)onion,onionlen);
+  
+    /* calculate number of cells required */
+    if (buflen%CELL_PAYLOAD_SIZE == 0)
+      cells = buflen/CELL_PAYLOAD_SIZE;
+    else
+      cells = buflen/CELL_PAYLOAD_SIZE+1;
+  
+    /* allocate memory for the cells */
+    *cellbuflen = cells * sizeof(cell_t);
+    *cellbuf = malloc(*cellbuflen);
+    if (!*cellbuf) /* malloc() error */
+      return -1;
+    
+    log(LOG_DEBUG,"pack_create() : Allocated memory for %u cells.",cells);
+  
+    /* create cells one by one */
+    dataleft = buflen;
+    for(i=0; i<cells; i++)
+    {
+      log(LOG_DEBUG,"pack_create() : Packing %u bytes of data.",dataleft);
+      if (dataleft >= CELL_PAYLOAD_SIZE)
+      {
+	c = new_create_cell(aci,CELL_PAYLOAD_SIZE,buf+i*CELL_PAYLOAD_SIZE);
+	dataleft -= CELL_PAYLOAD_SIZE;
+      }
+      else
+	c = new_create_cell(aci,dataleft,buf+i*CELL_PAYLOAD_SIZE);
+      
+      if (!c) /* cell creation failed */
+      {
+	free((void *)*cellbuf);
+	return -1;
+      } /* cell creation failed */
+      
+      log(LOG_DEBUG,"pack_create() : new_create_cell succeeded; copying the cell into output buffer");
+      /* cell has been created, now copy into buffer */
+      memcpy((void *)(*cellbuf+i*sizeof(cell_t)),(void *)c,sizeof(cell_t));
+      free((void *)c);
+    }
+  
+    free(buf);
+    return 0;
+  } /* valid parameters */
+  else /* invalid parameters */
+    return -1;
+}
+
+int pack_data(uint16_t aci,unsigned char *buf, size_t buflen, unsigned char **cellbuf, unsigned int *cellbuflen)
+{
+  cell_t *c;
+  unsigned int cells;
+  unsigned int dataleft;
+  unsigned int i;
+  
+  if ((aci) && (buf) && (cellbuf) && (cellbuflen) && (buflen)) /* valid parameters */
+  {
+    /* calculate number of cells required */
+    if (buflen%CELL_PAYLOAD_SIZE == 0)
+      cells = buflen/CELL_PAYLOAD_SIZE;
+    else
+      cells = buflen/CELL_PAYLOAD_SIZE+1;
+  
+    /* allocate memory for the cells */
+    *cellbuf = malloc(cells * sizeof(cell_t));
+    if (!*cellbuf) /* malloc() error */
+      return -1;
+
+    log(LOG_DEBUG,"pack_data() : Allocated memory for %u cells.",cells);
+    /* create cells one by one */
+    dataleft = buflen;
+    for(i=0; i<cells; i++)
+    {
+      log(LOG_DEBUG,"pack_data() : Packing %u bytes of data.",dataleft);
+      if (dataleft >= CELL_PAYLOAD_SIZE)
+      {
+	c = new_data_cell(aci,CELL_PAYLOAD_SIZE,buf+i*CELL_PAYLOAD_SIZE);
+	dataleft -= CELL_PAYLOAD_SIZE;
+      }
+      else
+	c = new_data_cell(aci,dataleft,buf+i*CELL_PAYLOAD_SIZE);
+
+      if (!c) /* cell creation failed */
+      {
+	free((void *)*cellbuf);
+	return -1;
+      } /* cell creation failed */
+      
+      /* cell has been created, now copy into buffer */
+      memcpy((void *)(*cellbuf+i*sizeof(cell_t)),(void *)c,sizeof(cell_t));
+      free((void *)c);
+    }
+  
+    *cellbuflen = cells * sizeof(cell_t);
+    return 0;
+  } /* valid parameters */
+  else /* invalid parameters */
+    return -1;
+}

+ 95 - 0
src/common/cell.h

@@ -0,0 +1,95 @@
+/**
+ * cell.h 
+ * Cell definition.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.14  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.13  2002/03/03 00:06:45  mp292
+ * Modifications to support re-transmission.
+ *
+ * Revision 1.12  2002/02/09 17:51:52  mp292
+ * CELL_ACK should be 4 not 3
+ *
+ * Revision 1.11  2002/02/03 22:41:45  mp292
+ * Changes to cell size.
+ *
+ * Revision 1.10  2002/01/21 20:57:19  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.9  2002/01/17 15:00:43  mp292
+ * Fixed a bug which caused malloc() generate a seg fault.
+ *
+ * Revision 1.8  2002/01/14 13:05:37  badbytes
+ * System testing in progress.
+ *
+ * Revision 1.7  2002/01/10 13:15:54  badbytes
+ * Fixed ACI size from 32bits to 16bits.
+ *
+ * Revision 1.6  2002/01/09 08:10:32  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.5  2002/01/07 13:03:28  badbytes
+ * cell.ACI is now cell.aci
+ *
+ * Revision 1.4  2002/01/07 09:26:00  badbytes
+ * Added pack_create() and pack_data().
+ *
+ * Revision 1.3  2002/01/07 07:48:34  badbytes
+ * fixed new_create_cell()
+ *
+ * Revision 1.2  2002/01/04 12:08:34  badbytes
+ * Added functions for cell creation.
+ *
+ * Revision 1.1  2002/01/04 10:02:07  badbytes
+ * Added cell definition.
+ *
+ */
+
+#ifndef __CELL_H
+
+#include <unistd.h>
+#include <stdint.h>
+
+/* cell commands */
+#define CELL_PADDING 0
+#define CELL_CREATE 1
+#define CELL_DATA 2
+#define CELL_DESTROY 3
+#define CELL_ACK 4
+#define CELL_NACK 5
+
+#define CELL_PAYLOAD_SIZE 120
+
+/* cell definition */
+typedef struct 
+{
+  uint16_t aci; /* Anonymous Connection Identifier */
+  unsigned char command;
+  unsigned char length; /* of payload */
+  uint32_t seq; /* sequence number */
+  unsigned char payload[120];
+} cell_t;
+
+cell_t *new_padding_cell(void);
+cell_t *new_create_cell(uint16_t aci, unsigned char length, unsigned char *buf);
+cell_t *new_destroy_cell(uint16_t aci);
+cell_t *new_data_cell(uint16_t aci, unsigned char length, unsigned char *buf);
+cell_t *new_ack_cell(uint16_t aci);
+cell_t *new_nack_cell(uint16_t aci);
+
+int pack_create(uint16_t aci, unsigned char *onion, uint32_t onionlen, unsigned char **cellbuf, unsigned int *cellbuflen);
+int pack_data(uint16_t aci, unsigned char *buf, size_t buflen, unsigned char **cellbuf, unsigned int *cellbuflen);
+
+#define __CELL_H
+#endif
+

+ 347 - 0
src/common/config.c

@@ -0,0 +1,347 @@
+/*
+ * config.c
+ * Functions for the manipulation of configuration files.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.7  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.6  2002/01/27 19:23:03  mp292
+ * Fixed a bug in parameter checking.
+ *
+ * Revision 1.5  2002/01/26 18:42:15  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.4  2002/01/21 21:07:56  mp292
+ * Parameter checking was missing in some functions.
+ *
+ * Revision 1.3  2001/12/07 09:38:03  badbytes
+ * Tested.
+ *
+ * Revision 1.2  2001/12/06 15:43:50  badbytes
+ * config.c compiles. Proceeding to test it.
+ *
+ * Revision 1.1  2001/11/22 01:20:27  mp292
+ * Functions for dealing with configuration files.
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "log.h"
+
+/* open configuration file for reading */
+FILE *open_config(const unsigned char *filename)
+{
+  FILE *f;
+
+  if (filename) /* non-NULL filename */
+  {
+    if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(filename)) /* filename consists of legal characters only */
+    {
+      f = fopen(filename, "r");
+      
+      return f;
+    } /* filename consists of legal characters only */
+    else /* illegal values in filename */
+    {
+      return NULL;
+    } /* illegal values in filename */
+  } /* non-NULL filename */
+  else /* NULL filename */
+    return NULL;
+}
+
+/* close configuration file */
+int close_config(FILE *f)
+{
+  int retval = 0;
+
+  if (f) /* valid file descriptor */
+  {
+    retval = fclose(f);
+  
+    return retval;
+  } /* valid file descriptor */
+  else
+    return -1;
+}
+
+/* parse the config file and obtain the required option values */
+int parse_config(FILE *f, config_opt_t *option)
+{
+  unsigned char keyword[CONFIG_KEYWORD_MAXLEN+1]; /* for storing the option keyword */
+  
+  unsigned char *buffer = NULL; /* option value */
+  size_t buflen = 0;
+  
+  char *errtest = NULL; /* used for testing correctness of strtol() etc. */
+  
+  unsigned int i_keyword = 0; /* current position within keyword */
+  unsigned int i_buf = 0; /* current position within buffer */
+  
+  char c=0; /* input char */
+  
+  unsigned int state=0; /* internal state
+		* 0 - trying to find a keyword
+		* 1 - reading a keyword
+		* 2 - keyword read and recognized, looking for the option value
+		* 3 - reading the option value
+		* 4 - option value read
+		* 5 - inside a comment
+		*/
+  
+  int retval=0; /* return value */
+  
+  int lineno=1; /* current line number */
+  int curopt=-1; /* current option, as an indexed in config_opt_t */
+  int i;
+  
+  if ( (f==NULL) || (option==NULL) ) /* invalid parameters */
+    return -1;
+  
+  fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */
+  
+  for (;;) /* infinite loop */
+  {
+    c = getc(f);
+    
+    if  ((c == '\n') || (c == EOF))
+    {
+      if (state == 1) /* reading a keyboard */
+      {
+	log(LOG_ERR,"Error parsing the configuration file on line %d.", lineno);
+	i_keyword = 0;
+	state = 0;
+	retval = -1;
+	break;
+      } /* reading a keyboard */
+      else if (state == 2) /* keyword read and recognized */
+      {
+	log(LOG_ERR,"Error parsing option %s on line %d.",option[curopt].keyword, lineno);
+	i_keyword = 0;
+	state = 0;
+	option[curopt].err=-1;
+	retval = -1;
+	break;
+      } /* keyboard read and recognized */
+      else if (state == 3) /* reading the option value */
+      {
+	buffer[i_buf++] = 0; /* add NULL character to terminate the string */
+	state = 4;
+	/* conversion and copying the value into config_opt_t is done later on */
+      } /* reading the option value */
+      else if (state == 5) /* reached end of comment */
+	state = 0;
+      
+      if (c == EOF)
+      {
+	log(LOG_DEBUG,"parse_config() : Reached eof on line %d.",lineno);
+	break;
+      } 
+      else
+      {
+	log(LOG_DEBUG,"parse_config() : Reached eol on line %d.", lineno);
+	lineno++;
+      }
+    }
+    else if ( (state==0) && (c == '#') ) /* lines beginning with # are ignored */
+    {
+      log(LOG_DEBUG,"parse_config() : Line %d begins with #.",lineno);
+      state = 5;
+    }
+    else if ( (state==0) && (isspace(c)) ) /* leading whitespace is ignored */
+      ;
+    else if ( (state==1) && (isspace(c)) ) /* have apparently read in all of the keyword */
+    {
+      keyword[i_keyword++] = 0;
+      curopt = -1;
+      for (i=0;option[i].keyword != NULL;i++) /* try and identify the keyword */
+      {
+	if (!strncmp(keyword,option[i].keyword,CONFIG_KEYWORD_MAXLEN))
+	{
+	  curopt = i;
+	  break;
+	}
+      } /* try and identify the keyword */
+      
+      if (curopt == -1) /* can't recognise the keyword */
+      {
+	log(LOG_ERR,"Error parsing the configuration file. Cannot recognize keyword %s on line %d.",keyword,lineno);
+	retval=-1;
+	break;
+      }
+      else
+	state = 2;
+    }
+    else if ( (state==2) && (isspace(c)) ) /* whitespace separating keyword and value is ignored */
+      ;
+    else if ( (state==3) && (isspace(c)) ) /* have apparently finished reading the option value */
+    {      
+      buffer[i_buf++]=0;
+      state = 4;
+    }
+    else /* all other characters */
+    { 
+      if (state == 0) /* first character of the keyword */
+      {
+	log(LOG_DEBUG, "parse_config() : %c is the start of a keyword on line %d.",c,lineno);
+	state = 1;
+	i_keyword = 0;
+	keyword[i_keyword++] = c;
+      }
+      else if (state == 1) /* keep on reading the keyword */
+      {
+	log(LOG_DEBUG,"parse_config() : %c is a character in the keyword on line %d.",c,lineno);
+	if (i_keyword < CONFIG_KEYWORD_MAXLEN) /* check for buffer overflow */
+	  keyword[i_keyword++] = c;
+	else
+	{
+	  log(LOG_ERR,"Error parsing the configuration file. Keyword on line %d exceeds %d characters.",lineno,CONFIG_KEYWORD_MAXLEN);
+	  retval=-1;
+	  break;
+	}
+      }
+      else if (state == 2) /* first character of the value */
+      {
+	log(LOG_DEBUG,"parse_config() : %c is the first character of the option value on line %d.",c,lineno);
+	state = 3;
+	i_buf=0;
+	buflen = CONFIG_VALUE_MAXLEN+1; /* allocate memory for the value buffer */
+	buffer = (char *)malloc(buflen);
+	if (!buffer)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  retval=-1;
+	  break;
+	} else
+	  buffer[i_buf++]=c;
+      }
+      else if (state == 3) /* keep on reading the value */
+      {
+	log(LOG_DEBUG,"parse_config() : %c is a character in the value of the keyword on line %d.",c,lineno);
+	if (i_buf >= buflen)
+	{
+	  log(LOG_ERR,"Length of keyword value on line %u exceeds the length limit (%u).",lineno, CONFIG_VALUE_MAXLEN);
+	  retval=-1;
+	  break;
+	}
+
+	buffer[i_buf++]=c;
+      }
+      else if (state == 5)
+	; /* character is part of a comment, skip */
+      else /* unexpected error */
+      {
+	log(LOG_ERR,"Unexpected error while parsing the configuration file.");
+	log(LOG_DEBUG,"parse_config() : Encountered a non-delimiter character while not in states 0,1,2 or 3!");
+	break;
+      }
+    }
+    
+    if (state==4) /* convert the value of the option to the appropriate type and write into OPT */
+    {
+      switch(option[curopt].r_type) /* consider each type separately */
+      {
+       case CONFIG_TYPE_STRING:
+	/* resize the buffer to fit the data exactly */
+	buffer = (char *)realloc(buffer,i_buf);
+	if (!buffer)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  return -1;
+	}
+	option[curopt].r.str = buffer;
+	option[curopt].err = 1;
+	break;
+	
+       case CONFIG_TYPE_CHAR:
+	option[curopt].r.c = *buffer;
+	option[curopt].err = 1;
+	break;
+	
+       case CONFIG_TYPE_INT:
+	errtest = NULL;
+	option[curopt].r.i = (int)strtol(buffer,&errtest,0);
+	if ((unsigned char *)errtest == buffer)
+	{
+	  log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
+	  option[curopt].err = -1;
+	  if (buffer)
+	    free(buffer);
+	  return -1;
+	}
+	else
+	  option[curopt].err = 1;
+	break;
+	
+       case CONFIG_TYPE_LONG:
+	errtest = NULL;
+	option[curopt].r.l = strtol(buffer,&errtest,0);
+	if ((unsigned char *)errtest == buffer)
+	{
+	  log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
+	  option[curopt].err = -1;
+	  if (buffer)
+	    free(buffer);
+	  return -1;
+	}
+	else
+	  option[curopt].err = 1;
+	break;
+	
+       case CONFIG_TYPE_DOUBLE:
+	errtest = NULL;
+	option[curopt].r.d = strtod(buffer,&errtest);
+	if ((unsigned char *)errtest == buffer)
+	{
+	  log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
+	  option[curopt].err = -1;
+	  if (buffer)
+	    free(buffer);
+	  return -1;
+	}
+	else
+	  option[curopt].err = 1;
+	break;
+	
+       default: /* unexpected type */
+	log(LOG_ERR, "Error parsing configuration file. Unrecognized option type!");
+	if (buffer)
+	  free(buffer);
+	return -1;
+      }
+      
+      /* clean up */
+      if (option[curopt].r_type != CONFIG_TYPE_STRING)
+      {
+	if (buffer)
+	  free(buffer);
+	buflen=0;
+      }
+      
+      state = 0;
+      curopt = -1;
+      i_buf=0;
+      i_keyword=0;
+    }
+    
+    
+  } /* infinite loop */
+  
+  return retval;
+}

+ 88 - 0
src/common/config.h

@@ -0,0 +1,88 @@
+/*
+ * config.h
+ * Functions for the manipulation of configuration files.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.7  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.6  2002/01/26 18:42:15  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.5  2002/01/21 21:07:56  mp292
+ * Parameter checking was missing in some functions.
+ *
+ * Revision 1.4  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.3  2001/12/07 09:38:03  badbytes
+ * Tested.
+ *
+ * Revision 1.2  2001/12/06 15:43:50  badbytes
+ * config.c compiles. Proceeding to test it.
+ *
+ * Revision 1.1  2001/11/22 01:20:27  mp292
+ * Functions for dealing with configuration files.
+ *
+ *
+ */
+
+#ifndef __CONFIG_H
+
+# include <stdio.h>
+
+/* enumeration of types which option values can take */
+#define CONFIG_TYPE_STRING  0
+#define CONFIG_TYPE_CHAR    1
+#define CONFIG_TYPE_INT     2
+#define CONFIG_TYPE_LONG    3
+#define CONFIG_TYPE_DOUBLE  4
+
+/* max. length of an option keyword */
+#define CONFIG_KEYWORD_MAXLEN 255
+
+/* max. length (in characters) of an option value */
+#define CONFIG_VALUE_MAXLEN 255
+
+/* legal characters in a filename */
+#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/"
+
+typedef struct
+{
+  unsigned char *keyword; /* option keyword */
+  
+  unsigned int r_type; /* return type as defined above */
+ 
+  union /* return value */
+  {
+    char *str;
+    char c;
+    int i;
+    long l;
+    double d;
+  } r;
+  
+  int err;      /*  1  OK
+		 *  0  keyword not found
+		 * -1  error while parsing */
+} config_opt_t;
+
+/* open configuration file for reading */
+FILE *open_config(const unsigned char *filename);
+
+/* close configuration file */
+int close_config(FILE *f);
+
+/* parse the config file and obtain required option values */
+int parse_config(FILE *f, config_opt_t *option);
+
+#define __CONFIG_H
+#endif

+ 89 - 0
src/common/key.c

@@ -0,0 +1,89 @@
+/**
+ * key.c 
+ * Key management.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.5  2002/03/12 23:28:26  mp292
+ * Removed calls to ERR_load_crypto_strings() (libcrypt).
+ *
+ * Revision 1.4  2002/01/27 19:23:03  mp292
+ * Fixed a bug in parameter checking.
+ *
+ * Revision 1.3  2002/01/26 18:50:11  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2002/01/04 07:19:03  badbytes
+ * Key generation moved to a separate utility (orkeygen).
+ *
+ * Revision 1.1  2001/12/14 12:16:33  badbytes
+ * Added routine for reading a private key from a file.
+ *
+ */
+
+#include <string.h>
+
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include "key.h"
+#include "log.h"
+#include "config.h"
+
+RSA *load_prkey(unsigned char *keyfile)
+{
+  RSA *rsa_private=NULL;
+  FILE *f_pr;
+  int retval = 0;
+  
+  if (keyfile) /* non-NULL filename */
+  {
+    if (strspn(keyfile,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(keyfile)) /* filename contains legal characters only */
+    {
+      /* open the keyfile */
+      f_pr=fopen(keyfile,"r");
+      if (!f_pr)
+      {
+	log(LOG_ERR,"Failed to open keyfile %s.",keyfile);
+	return NULL;
+      }
+      
+      /* read the private key */
+      rsa_private = PEM_read_RSAPrivateKey(f_pr,&rsa_private,NULL,NULL);
+      fclose(f_pr);
+      if (!rsa_private)
+      {
+	log(LOG_ERR,"Error reading private key : %s",ERR_reason_error_string(ERR_get_error()));
+	return NULL;
+      }
+      
+      /* check the private key */
+      retval = RSA_check_key(rsa_private);
+      if (retval == 0)
+      {
+	log(LOG_ERR,"Private key read but is invalid : %s.", ERR_reason_error_string(ERR_get_error()));
+	RSA_free(rsa_private);
+	return NULL;
+      }
+      else if (retval == -1)
+      {
+	log(LOG_ERR,"Private key read but validity checking failed : %s",ERR_reason_error_string(ERR_get_error()));
+	RSA_free(rsa_private);
+	return NULL;
+      }
+      else if (retval == 1)
+      {
+	return rsa_private;
+      }
+    } /* filename contains legal characters only */
+  }
+  
+  return NULL; /* report error */
+}

+ 32 - 0
src/common/key.h

@@ -0,0 +1,32 @@
+/**
+ * key.h 
+ * Routines for generating key pairs and loading private keys.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/01/26 18:50:11  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.1  2001/12/14 12:16:33  badbytes
+ * Added routine for reading a private key from a file.
+ *
+ */
+
+#ifndef __KEY_H
+#include <openssl/rsa.h>
+
+/* read the private key in keyfile into memory  */
+RSA *load_prkey(unsigned char *keyfile);
+
+#define __KEY_H
+#endif

+ 74 - 0
src/common/log.c

@@ -0,0 +1,74 @@
+/*
+ * log.c
+ * Logging facilities.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.11  2002/06/14 20:44:57  mp292
+ * *** empty log message ***
+ *
+ * Revision 1.10  2002/03/12 23:31:36  mp292
+ * *** empty log message ***
+ *
+ * Revision 1.9  2002/03/02 18:55:50  mp292
+ * LOG_DEBUG messages don't print the last errno error anymore.
+ *
+ * Revision 1.8  2002/01/26 22:46:48  mp292
+ * Reviewd according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.7  2002/01/17 15:00:43  mp292
+ * Fixed a bug which caused malloc() generate a seg fault.
+ *
+ * Revision 1.6  2001/12/12 16:02:55  badbytes
+ * Minor changes in output format.
+ *
+ * Revision 1.5  2001/12/12 06:48:07  badbytes
+ * Correction - last error message now only shown if severity==LOG_DEBUG.
+ *
+ * Revision 1.4  2001/12/12 06:28:46  badbytes
+ * Modified log() to print error message for last error in addition to the user-specified message.
+ *
+ * Revision 1.3  2001/12/07 09:38:03  badbytes
+ * Tested.
+ *
+ * Revision 1.2  2001/12/06 15:43:50  badbytes
+ * config.c compiles. Proceeding to test it.
+ *
+ * Revision 1.1  2001/11/21 23:03:41  mp292
+ * log function coded and tested.
+ * Top-level makefile.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include "log.h"
+
+void log_internal(int severity, const char *format, va_list ap);
+
+/* Outputs a message to stdout */
+void log(int severity, const char *format, ...)
+{
+  extern int loglevel;
+  va_list ap;
+
+  va_start(ap,format);
+
+  if (severity <= loglevel)
+  {
+    vprintf(format,ap);
+    printf("\n");
+  }
+  
+  va_end(ap);
+}

+ 40 - 0
src/common/log.h

@@ -0,0 +1,40 @@
+/*
+ * log.h
+ * Logging facilities.
+ * 
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.5  2002/01/26 18:52:00  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.4  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.3  2001/12/07 09:38:03  badbytes
+ * Tested.
+ *
+ * Revision 1.2  2001/12/06 15:43:50  badbytes
+ * config.c compiles. Proceeding to test it.
+ *
+ * Revision 1.1  2001/11/21 23:03:41  mp292
+ * log function coded and tested.
+ * Top-level makefile.
+ *
+ */
+
+#ifndef __LOG_H
+
+#include <syslog.h>
+
+/* Outputs a message to stdout and also logs the same message using syslog. */
+void log(int severity, const char *format, ...);
+
+# define __LOG_H
+#endif

+ 633 - 0
src/common/onion.c

@@ -0,0 +1,633 @@
+/**
+ * onion.c 
+ * Routines for creating/manipulating onions.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.24  2002/06/14 20:42:02  mp292
+ * Bug caused infinite loop when router list had <= 2 entries.
+ *
+ * Revision 1.23  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.22  2002/04/02 10:20:09  badbytes
+ * Memory overflow bug.
+ *
+ * Revision 1.21  2002/03/25 09:11:23  badbytes
+ * Added a list of onions being tracked for replay attacks.
+ *
+ * Revision 1.20  2002/03/12 23:32:41  mp292
+ * *** empty log message ***
+ *
+ * Revision 1.19  2002/01/27 19:23:21  mp292
+ * Fixed a bug in parameter checking.
+ *
+ * Revision 1.18  2002/01/26 19:24:29  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.17  2002/01/18 20:40:40  mp292
+ * Fixed a bug in en/decrypt_onion() functions.
+ *
+ * Revision 1.16  2002/01/17 23:48:31  mp292
+ * Added some extra debugging messages to fix a bug in encrypt_onion() which
+ * seems to corrupt the routent *route list.
+ *
+ * Revision 1.15  2002/01/17 15:00:43  mp292
+ * Fixed a bug which caused malloc() generate a seg fault.
+ *
+ * Revision 1.14  2002/01/16 23:01:54  mp292
+ * First phase of system testing completed (main functionality).
+ *
+ * Revision 1.13  2002/01/14 13:05:37  badbytes
+ * System testing in progress.
+ *
+ * Revision 1.12  2002/01/11 15:47:17  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.11  2002/01/09 07:58:23  badbytes
+ * Fixed a bug so that backward crypto engines are now initialized with DecryptInit.
+ *
+ * Revision 1.10  2002/01/09 07:55:23  badbytes
+ * Ciphers got out of sync. Hopefully fixed.
+ *
+ * Revision 1.9  2002/01/03 11:01:22  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.8  2001/12/19 08:29:00  badbytes
+ * Macro DEFAULT_CIPHER now holds the default crypto algorithm
+ *
+ * Revision 1.7  2001/12/18 14:12:47  badbytes
+ * Default cipher is now IDENTITY, for testing purposes.
+ *
+ * Revision 1.6  2001/12/18 10:36:51  badbytes
+ * create_onion() now also computes a crypt_path
+ *
+ * Revision 1.5  2001/12/17 13:35:17  badbytes
+ * Still writing handle_connection()
+ *
+ * Revision 1.4  2001/12/14 14:44:37  badbytes
+ * chooselen() tested
+ *
+ * Revision 1.3  2001/12/14 13:30:48  badbytes
+ * peel_onion() was redundant, removed it
+ *
+ * Revision 1.2  2001/12/14 13:14:03  badbytes
+ * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies.
+ *
+ * Revision 1.1  2001/12/14 12:41:12  badbytes
+ * Moved from op/ as it will be reused by other modules.
+ *
+ * Revision 1.1  2001/12/13 15:15:10  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <time.h>
+
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#include "onion.h"
+#include "log.h"
+
+/* uses a weighted coin with weight cw to choose a route length */
+int chooselen(double cw)
+{
+  int len = 2;
+  int retval = 0;
+  unsigned char coin;
+  
+  if ((cw < 0) || (cw >= 1)) /* invalid parameter */
+    return -1;
+  
+  while(1)
+  {
+    retval = RAND_pseudo_bytes(&coin,1);
+    if (retval == -1)
+      return -1;
+    
+    if (coin > cw*255) /* don't extend */
+      break;
+    else
+      len++;
+  }
+  
+  return len;
+}
+
+/* returns an array of pointers to routent that define a new route through the OR network
+ * int cw is the coin weight to use when choosing the route 
+ * order of routers is from last to first
+ */
+unsigned int *new_route(double cw, routent_t **rarray, size_t rarray_len, size_t *rlen)
+{
+  int routelen = 0;
+  int i = 0;
+  int retval = 0;
+  unsigned int *route = NULL;
+  unsigned int oldchoice, choice;
+  
+  if ( (cw >= 0) && (cw < 1) && (rarray) && (rlen) ) /* valid parameters */
+  {
+    routelen = chooselen(cw);
+    if (routelen == -1)
+    {
+      log(LOG_ERR,"Choosing route length failed.");
+      return NULL;
+    }
+    log(LOG_DEBUG,"new_route(): Chosen route length %u.",routelen);
+  
+    /* allocate memory for the new route */
+    route = (unsigned int *)malloc(routelen * sizeof(unsigned int));
+    if (!route)
+    {
+      log(LOG_ERR,"Memory allocation failed.");
+      return NULL;
+    }
+ 
+    oldchoice = rarray_len;
+    for(i=0;i<routelen;i++)
+    {
+      log(LOG_DEBUG,"new_route() : Choosing hop %u.",i);
+      retval = RAND_pseudo_bytes((unsigned char *)&choice,sizeof(unsigned int));
+      if (retval == -1)
+      {
+	free((void *)route);
+	return NULL;
+      }
+
+      choice = choice % (rarray_len);
+      log(LOG_DEBUG,"new_route() : Chosen router %u.",choice);
+      if (choice == oldchoice) /* same router */
+      {
+	/* try again  */
+	i--;
+	continue;
+      }
+      oldchoice = choice;
+      route[i] = choice;
+    }
+   
+    *rlen = routelen;
+    return route;
+  } /* valid parameters */
+  else /* invalid parameters */
+    return NULL;
+}
+
+/* creates a new onion from route, stores it and its length into bufp and lenp respectively */
+unsigned char *create_onion(routent_t **rarray, size_t rarray_len, unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp)
+{
+  int i,j;
+  int retval = 0;
+  onion_layer_t *layer = NULL;
+  crypt_path_t *hop = NULL;
+  unsigned char *retbuf = NULL;
+  unsigned char *bufp;
+  routent_t *router;
+
+  if ( (rarray) && (route) && (lenp) ) /* valid parameters */
+  {
+    /* calculate the size of the onion */
+    *lenp = routelen * 28 + 100; /* 28 bytes per layer + 100 bytes padding for the innermost layer */
+    log(LOG_DEBUG,"create_onion() : Size of the onion is %u.",*lenp);
+    
+    /* allocate memory for the onion */
+    bufp = (unsigned char *)malloc(*lenp);
+    if (!bufp)
+    {
+      log(LOG_ERR,"Error allocating memory.");
+      return NULL;
+    }
+    log(LOG_DEBUG,"create_onion() : Allocated memory for the onion.");
+    
+    for (retval=0; retval<routelen;retval++)
+    {
+      log(LOG_DEBUG,"create_onion() : %u : %s:%u, %u/%u",routelen-retval,inet_ntoa(*((struct in_addr *)&((rarray[route[retval]])->addr))),ntohs((rarray[route[retval]])->port),(rarray[route[retval]])->pkey,RSA_size((rarray[route[retval]])->pkey));
+    }
+    
+    layer = (onion_layer_t *)(bufp + *lenp - 128); /* pointer to innermost layer */
+    /* create the onion layer by layer, starting with the innermost */
+    for (i=0;i<routelen;i++)
+    {
+      router = rarray[route[i]];
+      
+      log(LOG_DEBUG,"create_onion() : %u",router);
+      log(LOG_DEBUG,"create_onion() : This router is %s:%u",inet_ntoa(*((struct in_addr *)&router->addr)),ntohs(router->port));
+      log(LOG_DEBUG,"create_onion() : Key pointer = %u.",router->pkey);
+      log(LOG_DEBUG,"create_onion() : Key size = %u.",RSA_size(router->pkey)); 
+      
+      /* 0 bit */
+      layer->zero = 0;
+      /* version */
+      layer->version = VERSION;
+      /* Back F + Forw F both use DES OFB*/
+      layer->backf = ONION_DEFAULT_CIPHER;
+      layer->forwf = ONION_DEFAULT_CIPHER;
+      /* Dest Port */
+      if (i) /* not last hop */
+	layer->port = rarray[route[i-1]]->port;
+      else
+	layer->port = 0;
+      /* Dest Addr */
+      if (i) /* not last hop */
+	layer->addr = rarray[route[i-1]]->addr;
+      else
+	layer->addr = 0;
+      /* Expiration Time */
+      layer->expire = time(NULL) + 3600; /* NOW + 1 hour */
+      /* Key Seed Material */
+      retval = RAND_bytes(layer->keyseed,16);
+      if (retval < 1) /* error */
+      {
+	log(LOG_ERR,"Error generating random data.");
+	free((void *)bufp);
+	if (cpathp)
+	{
+	  for (j=0;j<i;j++)
+	    free((void *)cpathp[i]);
+	}
+	return NULL;
+      }
+      log(LOG_DEBUG,"create_onion() : Onion layer %u built : %u, %u, %u, %s, %u.",i+1,layer->zero,layer->backf,layer->forwf,inet_ntoa(*((struct in_addr *)&layer->addr)),ntohs(layer->port));
+      
+      /* build up the crypt_path */
+      if (cpathp)
+      {
+	cpathp[i] = (crypt_path_t *)malloc(sizeof(crypt_path_t));
+	if (!cpathp[i])
+	{
+	  log(LOG_ERR,"Error allocating memory.");
+	  free((void *)bufp);
+	  for (j=0;j<i;j++)
+	    free((void *)cpathp[i]);
+	}
+      
+	log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1);
+	hop = cpathp[i];
+	/* set crypto functions */
+	hop->backf = layer->backf;
+	hop->forwf = layer->forwf;
+	
+	/* calculate keys */
+	SHA1(layer->keyseed,16,hop->digest3);
+	log(LOG_DEBUG,"create_onion() : First SHA pass performed.");
+	SHA1(hop->digest3,20,hop->digest2);
+	log(LOG_DEBUG,"create_onion() : Second SHA pass performed.");
+	SHA1(hop->digest2,20,hop->digest3);
+	log(LOG_DEBUG,"create_onion() : Third SHA pass performed.");
+	log(LOG_DEBUG,"create_onion() : Keys generated.");
+	/* set IVs */
+	memset((void *)hop->f_iv,0,16);
+	memset((void *)hop->b_iv,0,16);
+	
+	/* initialize cipher contexts */
+	EVP_CIPHER_CTX_init(&hop->f_ctx);
+	EVP_CIPHER_CTX_init(&hop->b_ctx);
+	
+	/* initialize cipher engines */
+	switch(layer->forwf)
+	{
+	 case ONION_CIPHER_DES :
+	  retval = EVP_EncryptInit(&hop->f_ctx, EVP_des_ofb(), hop->digest3, hop->f_iv);
+	  break;
+	 case ONION_CIPHER_RC4 :
+	  retval = EVP_EncryptInit(&hop->f_ctx, EVP_rc4(), hop->digest3, hop->f_iv);
+	  break;
+	 case ONION_CIPHER_IDENTITY :
+	  retval = EVP_EncryptInit(&hop->f_ctx, EVP_enc_null(), hop->digest3, hop->f_iv);
+	  break;
+	}
+	if (!retval) /* cipher initialization failed */
+	{
+	  log(LOG_ERR,"Could not initialize crypto engines.");
+	  free((void *)bufp);
+	  for (j=0;j<i;j++)
+	    free((void *)cpathp[i]);
+	  return NULL;
+	}
+	switch(layer->backf)
+	{
+	 case ONION_CIPHER_DES :
+	  retval = EVP_DecryptInit(&hop->b_ctx, EVP_des_ofb(), hop->digest2, hop->b_iv);
+	  break;
+	 case ONION_CIPHER_RC4 :
+	  retval = EVP_DecryptInit(&hop->b_ctx, EVP_rc4(), hop->digest2, hop->b_iv);
+	  break;
+	 case ONION_CIPHER_IDENTITY :
+	  retval = EVP_DecryptInit(&hop->b_ctx, EVP_enc_null(), hop->digest2, hop->b_iv);
+	  break;
+	}
+	if (!retval) /* cipher initialization failed */
+	{
+	  log(LOG_ERR,"Could not initialize crypto engines.");
+	  free((void *)bufp);
+	  for (j=0;j<i;j++)
+	    free((void *)cpathp[i]);
+	  return NULL;
+	}
+	
+	log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop.");
+      }
+      
+      /* padding if this is the innermost layer */
+      if (!i)
+      {
+	retval=RAND_pseudo_bytes((unsigned char *)layer + 28,100);
+	if (retval == -1) /* error */
+	{
+	  log(LOG_ERR,"Error generating pseudo-random data.");
+	  free((void *)bufp);
+	  if (cpathp)
+	  {
+	    for (j=0;j<i;j++)
+	      free((void *)cpathp[i]);
+	  }
+	  return NULL;
+	}
+	log(LOG_DEBUG,"create_onion() : This is the innermost layer. Adding 100 bytes of padding.");
+      }
+      
+      /* encrypt */
+      retbuf = encrypt_onion(layer,128+(i*28),router->pkey);
+      if (!retbuf)
+      {
+	log(LOG_ERR,"Error encrypting onion layer.");
+	free((void *)bufp);
+	if (cpathp)
+	{
+	  for (j=0;j<i;j++)
+	    free((void *)cpathp[i]);
+	}
+	return NULL;
+      }
+      log(LOG_DEBUG,"create_onion() : Encrypted layer.");
+      
+      /* calculate pointer to next layer */
+      layer = (onion_layer_t *)bufp + (routelen-i-2)*sizeof(onion_layer_t);
+    }
+
+    return bufp;
+  } /* valid parameters */
+  else
+    return NULL;
+}
+
+/* encrypts 128 bytes of the onion with the specified public key, the rest with 
+ * DES OFB with the key as defined in the outter layer */
+unsigned char *encrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *pkey)
+{
+  unsigned char *tmpbuf = NULL; /* temporary buffer for crypto operations */
+  unsigned char digest[20]; /* stores SHA1 output - 160 bits */
+  unsigned char *retbuf = NULL;
+  unsigned char iv[8];
+  int retval = 0;
+  int outlen = 0;
+  
+  EVP_CIPHER_CTX ctx; /* cipher context */
+ 
+  if ( (onion) && (pkey) ) /* valid parameters */
+  {
+    memset((void *)iv,0,8);
+    
+    log(LOG_DEBUG,"Onion layer : %u, %u, %u, %s, %u.",onion->zero,onion->backf,onion->forwf,inet_ntoa(*((struct in_addr *)&onion->addr)),ntohs(onion->port));
+    /* allocate space for tmpbuf */
+    tmpbuf = (unsigned char *)malloc(onionlen);
+    if (!tmpbuf)
+    {
+      log(LOG_ERR,"Could not allocate memory.");
+      return NULL;
+    }
+    log(LOG_DEBUG,"encrypt_onion() : allocated %u bytes of memory for the encrypted onion (at %u).",onionlen,tmpbuf);
+  
+    /* get key1 = SHA1(KeySeed) */
+    retbuf = SHA1(((onion_layer_t *)onion)->keyseed,16,digest);
+    if (!retbuf)
+    {
+      log(LOG_ERR,"Error computing SHA1 digest.");
+      free((void *)tmpbuf);
+      return NULL;
+    }
+    log(LOG_DEBUG,"encrypt_onion() : Computed DES key.");
+    
+    log(LOG_DEBUG,"encrypt_onion() : Trying to RSA encrypt.");
+    /* encrypt 128 bytes with RSA *pkey */
+    retval = RSA_public_encrypt(128, (unsigned char *)onion, tmpbuf, pkey, RSA_NO_PADDING); 
+    if (retval == -1) 
+    { 
+      log(LOG_ERR,"Error RSA-encrypting data :%s",ERR_reason_error_string(ERR_get_error())); 
+      free((void *)tmpbuf); 
+      return NULL; 
+    }
+  
+    log(LOG_DEBUG,"encrypt_onion() : RSA encrypted first 128 bytes of the onion."); 
+    
+    /* now encrypt the rest with DES OFB */
+    
+    EVP_CIPHER_CTX_init(&ctx); 
+    retval = EVP_EncryptInit(&ctx,EVP_des_ofb(),digest,iv);
+    if (!retval) /* error */ 
+    { 
+      log(LOG_ERR,"Error initializing DES engine:%s",ERR_reason_error_string(ERR_get_error())); 
+      free((void *)tmpbuf); 
+      return NULL; 
+    } 
+    
+    retval = EVP_EncryptUpdate(&ctx,(unsigned char *)tmpbuf+128,&outlen,(unsigned char *)onion+128,onionlen-128);
+    if (!retval) /* error */
+    { 
+      log(LOG_ERR,"Error performing DES encryption:%s",ERR_reason_error_string(ERR_get_error())); 
+      free((void *)tmpbuf); 
+      return NULL; 
+    }
+    log(LOG_DEBUG,"encrypt_onion() : DES OFB encrypted the rest of the onion.");
+  
+    EVP_CIPHER_CTX_cleanup(&ctx);
+  
+    /* now copy tmpbuf to onion */
+    memcpy((void *)onion,(void *)tmpbuf,onionlen);
+    log(LOG_DEBUG,"encrypt_onion() : Copied cipher to original onion buffer.");
+    free((void *)tmpbuf);
+    return (unsigned char *)onion;
+  } /* valid parameters */
+  else
+    return NULL;
+}
+
+/* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */
+unsigned char *decrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *prkey)
+{
+  void *tmpbuf = NULL; /* temporary buffer for crypto operations */
+  unsigned char digest[20]; /* stores SHA1 output - 160 bits */
+  unsigned char *retbuf = NULL;
+  unsigned char iv[8];
+  int retval = 0;
+  int outlen = 0;
+  
+  EVP_CIPHER_CTX ctx; /* cipher context */
+  
+  if ( (onion) && (prkey) ) /* valid parameters */
+  {
+    memset((void *)iv,0,8);
+    
+    /* allocate space for tmpbuf */
+    tmpbuf = malloc(onionlen);
+    if (!tmpbuf)
+    {
+      log(LOG_ERR,"Could not allocate memory.");
+      return NULL;
+    }
+    log(LOG_DEBUG,"decrypt_onion() : Allocated memory for the temporary buffer.");
+
+    /* decrypt 128 bytes with RSA *prkey */
+    retval = RSA_private_decrypt(128, (unsigned char*)onion, (unsigned char *)tmpbuf, prkey, RSA_NO_PADDING);
+    if (retval == -1)
+    {
+      log(LOG_ERR,"Error RSA-decrypting data :%s",ERR_reason_error_string(ERR_get_error()));
+      free((void *)tmpbuf);
+      return NULL;
+    }
+    log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete.");
+    
+    /* get key1 = SHA1(KeySeed) */
+    retbuf = SHA1(((onion_layer_t *)tmpbuf)->keyseed,16,digest);
+    if (!retbuf)
+    {
+      log(LOG_ERR,"Error computing SHA1 digest.");
+      free((void *)tmpbuf);
+      return NULL;
+    }
+    log(LOG_DEBUG,"decrypt_onion() : Computed DES key.");
+    
+    /* now decrypt the rest with DES OFB */
+    EVP_CIPHER_CTX_init(&ctx);
+    retval = EVP_DecryptInit(&ctx,EVP_des_ofb(),digest,iv);
+    if (!retval) /* error */
+    {
+      log(LOG_ERR,"Error initializing DES engine:%s",ERR_reason_error_string(ERR_get_error()));
+      free((void *)tmpbuf);
+      return NULL;
+    }
+    retval = EVP_DecryptUpdate(&ctx,(unsigned char *)tmpbuf+128,&outlen,(unsigned char *)onion+128,onionlen-128);
+    if (!retval) /* error */
+    {
+      log(LOG_ERR,"Error performing DES decryption:%s",ERR_reason_error_string(ERR_get_error()));
+      free((void *)tmpbuf);
+      return NULL;
+    }
+    
+    EVP_CIPHER_CTX_cleanup(&ctx);
+    log(LOG_DEBUG,"decrypt_onion() : DES decryption complete.");
+    
+    /* now copy tmpbuf to onion */
+    memcpy((void *)onion,(void *)tmpbuf,onionlen);
+    free((void *)tmpbuf);
+    return (unsigned char *)onion;
+  } /* valid parameters */
+  else
+    return NULL;
+}
+
+/* delete first n bytes of the onion and pads the end with n bytes of random data */
+void pad_onion(unsigned char *onion, uint32_t onionlen, size_t n)
+{
+  if (onion) /* valid parameter */
+  {
+    memmove((void *)onion,(void *)(onion+n),onionlen-n);
+    RAND_pseudo_bytes(onion+onionlen-n,n);
+  }
+}
+
+/* create a new tracked_onion entry */
+tracked_onion_t *new_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion)
+{
+  tracked_onion_t *to = NULL;
+  
+  if (!onion || !tracked_onions || !last_tracked_onion) /* invalid parameters */
+    return NULL;
+  
+  to = (tracked_onion_t *)malloc(sizeof(tracked_onion_t));
+  if (!to)
+    return NULL;
+  
+  to->expire = ((onion_layer_t *)onion)->expire; /* set the expiration date */
+  /* compute the SHA digest */
+  SHA1(onion, onionlen, to->digest);
+  if (!to->digest)
+  {
+    log(LOG_DEBUG,"new_tracked_onion() : Failed to compute a SHA1 digest of the onion.");
+    free((void *)to);
+    return NULL;
+  }
+  
+  to->next = NULL;
+  
+  if (!*tracked_onions)
+  {
+    to->prev = NULL;
+    *tracked_onions = to;
+  }
+  else
+  {
+    to->prev = (void *)*last_tracked_onion;
+    (*last_tracked_onion)->next = (void *)to;
+  }
+  *last_tracked_onion = to;
+  
+  return to;
+}
+
+/* delete a tracked onion entry */
+void remove_tracked_onion(tracked_onion_t *to, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion)
+{
+  if (!*tracked_onions || !*last_tracked_onion || !to)
+    return;
+  
+  if (to->prev)
+    ((tracked_onion_t *)to->prev)->next = to->next;
+  if (to->next)
+    ((tracked_onion_t *)to->next)->prev = to->prev;
+  
+  if (to == *tracked_onions)
+    *tracked_onions = (tracked_onion_t *)to->next;
+  
+  if (to == *last_tracked_onion)
+    *last_tracked_onion = (tracked_onion_t *)to->prev;
+  
+  free((void *)to);
+  
+  return;
+}
+
+/* find a tracked onion in the linked list of tracked onions */
+tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t *tracked_onions)
+{
+  tracked_onion_t *to = tracked_onions;
+  unsigned char digest[20];
+  
+  /* compute the SHA digest of the onion */
+  SHA1(onion,onionlen, digest);
+  
+  while(to)
+  {
+    if (!memcmp((void *)digest, (void *)to->digest, 20))
+      return to;
+    to = (tracked_onion_t *)to->next;
+  }
+  
+  return NULL;
+}
+

+ 156 - 0
src/common/onion.h

@@ -0,0 +1,156 @@
+/**
+ * onion.h 
+ * Routines for creating/manipulating onions.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.17  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.16  2002/03/25 09:11:23  badbytes
+ * Added a list of onions being tracked for replay attacks.
+ *
+ * Revision 1.15  2002/01/26 19:24:29  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.14  2002/01/18 20:40:40  mp292
+ * Fixed a bug in en/decrypt_onion() functions.
+ *
+ * Revision 1.13  2002/01/17 23:48:31  mp292
+ * Added some extra debugging messages to fix a bug in encrypt_onion() which
+ * seems to corrupt the routent *route list.
+ *
+ * Revision 1.12  2002/01/11 15:47:17  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.11  2002/01/09 07:55:23  badbytes
+ * Ciphers got out of sync. Hopefully fixed.
+ *
+ * Revision 1.10  2002/01/04 13:48:54  badbytes
+ * Changed unsigned short/long to uint16_t and uint32_t respectively.
+ *
+ * Revision 1.9  2001/12/19 08:29:00  badbytes
+ * Macro DEFAULT_CIPHER now holds the default crypto algorithm
+ *
+ * Revision 1.8  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.7  2001/12/18 07:26:47  badbytes
+ * Added a new definition of onion_layer_t, depending on the byte order.
+ *
+ * Revision 1.6  2001/12/17 13:35:17  badbytes
+ * Still writing handle_connection()
+ *
+ * Revision 1.5  2001/12/14 14:44:37  badbytes
+ * chooselen() tested
+ *
+ * Revision 1.4  2001/12/14 13:31:08  badbytes
+ * peel_onion() was redundant, removed it
+ *
+ * Revision 1.3  2001/12/14 13:14:03  badbytes
+ * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies.
+ *
+ * Revision 1.2  2001/12/14 12:44:47  badbytes
+ * Minor modifications to reflect new paths ...
+ *
+ * Revision 1.1  2001/12/14 12:41:12  badbytes
+ * Moved from op/ as it will be reused by other modules.
+ *
+ * Revision 1.1  2001/12/13 15:15:10  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#ifndef __ONION_H
+
+#include <endian.h>
+#include <stdint.h>
+
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+
+#include "routent.h"
+#include "version.h"
+
+/* available cipher functions */
+#define ONION_CIPHER_IDENTITY 0
+#define ONION_CIPHER_DES 1
+#define ONION_CIPHER_RC4 2
+
+/* default cipher function */
+#define ONION_DEFAULT_CIPHER ONION_CIPHER_DES
+
+typedef struct
+{
+  int zero:1;
+  int version:7;
+  int backf:4;
+  int forwf:4;
+  uint16_t port;
+  uint32_t addr;
+  time_t expire;
+  unsigned char keyseed[16];
+} onion_layer_t;
+
+typedef struct
+{
+  unsigned int forwf;
+  unsigned int backf;
+  char digest2[20]; /* second SHA output for onion_layer_t.keyseed */
+  char digest3[20]; /* third SHA output for onion_layer_t.keyseed */
+  
+  /* IVs */
+  char f_iv[16];
+  char b_iv[16];
+  
+  /* cipher contexts */
+  EVP_CIPHER_CTX f_ctx;
+  EVP_CIPHER_CTX b_ctx;
+  
+} crypt_path_t;
+
+typedef struct
+{
+  time_t expire;
+  char digest[20]; /* SHA digest of the onion */
+  void *prev;
+  void *next;
+} tracked_onion_t;
+
+/* returns an array of indexes into a router array that define a new route through the OR network
+ * int cw is the coin weight to use when choosing the route 
+ * order of routers is from last to first */
+unsigned int *new_route(double cw, routent_t **rarray, size_t rarray_len, size_t *rlen);
+
+/* creates a new onion from route, stores it and its length into bufp and lenp respectively */
+/* if cpathp not NULL then also compute the corresponding crypt_path */ 
+unsigned char *create_onion(routent_t **rarray, size_t rarray_len, unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp);
+
+/* encrypts 128 bytes of the onion with the specified public key, the rest with 
+ * DES OFB with the key as defined in the outter layer */
+unsigned char *encrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *pkey);
+
+/* decrypts the onion */
+unsigned char *decrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *prkey);
+
+/* deletes the first n bytes of the onion and pads the end with n bytes of random data */
+void pad_onion(unsigned char *onion, uint32_t onionlen, size_t n);
+
+/* create a new tracked_onion entry */
+tracked_onion_t *new_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion);
+
+/* delete a tracked onion entry */
+void remove_tracked_onion(tracked_onion_t *to, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion);
+
+/* find a tracked onion in the linked list of tracked onions */
+tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t *tracked_onions);
+
+#define __ONION_H
+#endif

+ 77 - 0
src/common/opcell.c

@@ -0,0 +1,77 @@
+/**
+ * opcell.c
+ * Onion Proxy Cell
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/03/03 12:08:18  mp292
+ * Added a new type of cell - used for data going between the onion proxy and
+ * the first or hop. Payload size identical to that of a normal cell.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+  
+#include <openssl/rand.h>
+
+#include "opcell.h"
+#include "log.h"
+
+opcell_t *new_padding_opcell()
+{
+  opcell_t *c = NULL;
+  int retval;
+  
+  c = malloc(sizeof(opcell_t));
+  if (!c) /* malloc() error */
+    return NULL;
+
+  retval = RAND_pseudo_bytes((unsigned char *)c,sizeof(opcell_t));
+  if (retval == -1) /* RAND_pseudo_bytes() error */
+  {
+    free((void *)c);
+    return NULL;
+  } /* RAND_pseudo_bytes() error */
+
+  c->command = OPCELL_PADDING;
+  
+  return c;
+}
+
+opcell_t *new_data_opcell(unsigned char length, unsigned char *buf)
+{
+  opcell_t *c = NULL;
+  int retval;
+
+  if ((length <= OPCELL_PAYLOAD_SIZE) && (buf)) /* valid parameters */
+  {
+    c = malloc(sizeof(opcell_t));
+    if (!c) /* malloc() error */
+      return NULL;
+    
+    c->command = OPCELL_DATA;
+    c->length = length;
+    
+    memcpy((void *)c->payload, (void *)buf, length);
+    retval = RAND_pseudo_bytes((unsigned char *)(c->payload+length),OPCELL_PAYLOAD_SIZE-length);
+    if (retval == -1) /* RAND_pseudo_bytes() error */
+    {
+      free((void *)c);
+      return NULL;
+    } /* RAND_pseudo_bytes() error */
+    
+    return c;
+  } /* valid parameters */
+  else /* invalid parameters */
+    return NULL;
+}
+

+ 44 - 0
src/common/opcell.h

@@ -0,0 +1,44 @@
+/**
+ * opcell.h 
+ * Onion Proxy Cell.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/03/03 12:08:18  mp292
+ * Added a new type of cell - used for data going between the onion proxy and
+ * the first or hop. Payload size identical to that of a normal cell.
+ *
+ */
+
+#ifndef __OPCELL_H
+
+#include <stdint.h>
+
+#include "cell.h"
+
+#define OPCELL_PAYLOAD_SIZE CELL_PAYLOAD_SIZE
+
+#define OPCELL_PADDING 0
+#define OPCELL_DATA 1
+
+/* cell definition */
+typedef struct 
+{
+  unsigned char command;
+  unsigned char length; /* of payload */
+  unsigned char payload[OPCELL_PAYLOAD_SIZE];
+} opcell_t;
+
+opcell_t *new_data_opcell(unsigned char length, unsigned char *buf);
+opcell_t *new_padding_opcell();
+
+#define __OPCELL_H
+#endif
+

+ 39 - 0
src/common/policies.h

@@ -0,0 +1,39 @@
+/**
+ * policies.h
+ * Traffic shaping policies for the network funnel.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.2  2002/03/12 23:42:37  mp292
+ * Various bugfixes.
+ *
+ * Revision 1.1  2002/03/03 00:03:49  mp292
+ * Moved from or/network (merged core and network funnel into a single thread).
+ *
+ * Revision 1.3  2002/02/09 17:00:42  mp292
+ * Added core_sock to list of parameters for comms with the router core.
+ *
+ * Revision 1.2  2002/02/03 22:40:44  mp292
+ * Changes to cell size.
+ *
+ * Revision 1.1  2002/02/03 20:34:38  mp292
+ * Traffic shaping policies for the network funnel.
+ * 
+ */
+
+
+/* traffic shaping policies */
+#define POLICY_DROP_CONNECTIONS 0 /* buffer data and drop the connections that cannot be allocated resources */
+#define POLICY_DROP_CELLS 1 /* buffer data and drop cells, which can't be bufered, do re-transmission */
+
+#define DEFAULT_POLICY POLICY_DROP_CONNECTIONS
+
+#define DEFAULT_ACK_TIMEOUT 3000 /* ms */
+#define DEFAULT_WINDOW_SIZE 5 /* cells */

+ 147 - 0
src/common/routent.c

@@ -0,0 +1,147 @@
+/*
+ * routent.c
+ * Onion Router and related definitions.
+ * 
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.6  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.5  2002/03/12 23:38:54  mp292
+ * Being pedantic about some pointer conversions.
+ *
+ * Revision 1.4  2002/03/03 00:24:26  mp292
+ * Corrected paths to some #include files.
+ *
+ * Revision 1.3  2002/03/03 00:06:45  mp292
+ * Modifications to support re-transmission.
+ *
+ * Revision 1.2  2002/01/26 19:26:55  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.1  2002/01/10 08:28:33  badbytes
+ * routent and routentEX related routines
+ *
+ */
+
+#include "policies.h"
+
+#include "routent.h"
+
+routentEX_t *id_router(routentEX_t **routerarray, size_t rarray_len, uint32_t addr, uint16_t port)
+{
+  routentEX_t *router;
+  int i;
+  
+  if (!routerarray)
+    return NULL;
+  
+  for(i=0;i<rarray_len;i++)
+  {
+    router = routerarray[i];
+    if ((router->addr == addr) && (router->port == port))
+      return router;
+  }
+  
+  return NULL;
+}
+
+routentEX_t *id_routerbys(routentEX_t **routerarray, size_t rarray_len, int s)
+{
+  routentEX_t *router;
+  int i;
+  
+  if (!routerarray)
+    return NULL;
+  
+  for(i=0;i<rarray_len;i++)
+  {
+    router = routerarray[i];
+    if (router->s == s)
+      return router;
+  }
+  
+  return NULL;
+}
+
+conn_buf_t *new_conn_buf(uint16_t aci, int policy, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf)
+{
+  conn_buf_t *conn_buf;
+  
+  if ((!aci) || (!conn_bufs) || (!last_conn_buf)) /* invalid parameters */
+    return NULL;
+  
+  conn_buf = (conn_buf_t *)malloc(sizeof(conn_buf_t));
+  if (!conn_buf)
+    return NULL;
+  
+  memset((void *)conn_buf,0,sizeof(conn_buf_t));
+  conn_buf->win_size = DEFAULT_WINDOW_SIZE;
+  conn_buf->win_avail = DEFAULT_WINDOW_SIZE;
+  conn_buf->aci = aci;
+  conn_buf->policy = policy;
+  
+  if (!*conn_bufs)
+  {
+    *conn_bufs = conn_buf;
+  }
+  else
+  {
+    (*last_conn_buf)->next=(void *)conn_buf;
+    conn_buf->prev = (void *)*last_conn_buf;
+  }
+  
+  *last_conn_buf = conn_buf;
+  
+  return conn_buf;
+}
+
+int remove_conn_buf(conn_buf_t *conn_buf, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf)
+{
+  if ( (!conn_buf) || (!*conn_bufs) || (!*last_conn_buf) ) /* invalid parameters */
+    return -1;
+  
+  if (conn_buf->next)
+    ((conn_buf_t *)(conn_buf->next))->prev = conn_buf->prev;
+  if (conn_buf->prev)
+    ((conn_buf_t *)(conn_buf->prev))->next = conn_buf->next;
+  
+  if (conn_buf == *last_conn_buf)
+    *last_conn_buf = (conn_buf_t *)conn_buf->prev;
+  
+  if (conn_buf == *conn_bufs)
+    *conn_bufs = (conn_buf_t *)conn_buf->next;
+
+  if (conn_buf->buf)
+    free((void *)conn_buf->buf);
+  
+  free((void *)conn_buf);
+  
+  return 0;
+}
+
+conn_buf_t *id_conn_buf(conn_buf_t *conn_bufs, uint16_t aci)
+{
+  conn_buf_t *conn_buf;
+  
+  if ( (!aci) || (!conn_bufs) )
+    return NULL;
+  
+  conn_buf = conn_bufs;
+  while (conn_buf)
+  {
+    if (conn_buf->aci == aci)
+      return conn_buf;
+    
+    conn_buf = conn_buf->next;
+  }
+  
+  return NULL;
+}

+ 189 - 0
src/common/routent.h

@@ -0,0 +1,189 @@
+/*
+ * routent.h
+ * Onion Router and related definitions.
+ * 
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.25  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.24  2002/03/29 09:54:19  badbytes
+ * Fixed type of routentEX.min_interval to struct timeval.
+ *
+ * Revision 1.23  2002/03/21 07:20:59  badbytes
+ * Added a dependency to <sys/time.h>.
+ *
+ * Revision 1.22  2002/03/12 23:37:14  mp292
+ * Additional flag - destory_buf saying whether the buffer should be destroyed
+ * when the destroy cell is sent.
+ *
+ * Revision 1.21  2002/03/03 00:06:45  mp292
+ * Modifications to support re-transmission.
+ *
+ * Revision 1.20  2002/02/09 16:58:53  mp292
+ * Postponed implementtion of POLICY_DROP_CONNECTIONS due to problems. Need to
+ * discuss with Andrei first.
+ *
+ * Revision 1.19  2002/02/09 16:54:59  mp292
+ * routentEX now contains a per anonymous connection packet count
+ *
+ * Revision 1.18  2002/01/29 00:59:16  mp292
+ * Slight changes in the way timers are kept, c.f. changes in the network funnel.
+ *
+ * Revision 1.17  2002/01/28 21:37:36  mp292
+ * Router's output buffer is now dynamic. Time of last output to the router
+ * added to routentEX.
+ *
+ * Revision 1.16  2002/01/26 19:26:55  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.15  2002/01/18 22:55:40  mp292
+ * Added a cell buffer to struct routent so that a cell can be received in
+ * several bursts of data. This prevents a DoS attack on the network funnel.
+ *
+ * Revision 1.14  2002/01/14 13:05:37  badbytes
+ * System testing in progress.
+ *
+ * Revision 1.13  2002/01/11 15:47:17  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.12  2002/01/10 08:28:33  badbytes
+ * routent and routentEX related routines
+ *
+ * Revision 1.11  2002/01/08 15:13:30  badbytes
+ * Added cipher context to routentEX
+ *
+ * Revision 1.10  2002/01/08 13:18:48  badbytes
+ * Added a connection buffer to routentEX
+ *
+ * Revision 1.9  2002/01/08 13:02:16  badbytes
+ * routentEX now contains f_key and b_key, 56-bit DES keys for link encryption
+ *
+ * Revision 1.8  2002/01/03 11:17:01  badbytes
+ * routentEX.max and routentEX.min values changed to 32bit not 64bit.
+ *
+ * Revision 1.7  2002/01/03 11:04:16  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.6  2002/01/03 11:03:14  badbytes
+ * Added an extended version of routent which includes link utilisation info.
+ *
+ * Revision 1.5  2001/12/18 15:26:34  badbytes
+ * Added #inclusion of <stdint.h>
+ *
+ * Revision 1.4  2001/12/18 15:19:41  badbytes
+ * In struct routent, changed long and short types to uint32_t and uint16_t
+ *
+ * Revision 1.3  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.2  2001/12/17 13:35:17  badbytes
+ * Still writing handle_connection()
+ *
+ * Revision 1.1  2001/12/14 13:14:03  badbytes
+ * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies.
+ *
+ */
+
+#ifndef __ROUTENT_H
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/timeb.h>
+
+#include "cell.h"
+
+/* per-anonymous-connection cell buffer */
+typedef struct
+{
+  uint16_t aci;
+  int policy;
+  unsigned int cells;
+  unsigned char *buf;
+  unsigned int buflen;
+  unsigned int offset; /* offset to the position of the first cell in the buffer */
+  cell_t dc; /* static buffer for the destroy cell - so we are always able to destroy a connection */
+  unsigned char dc_set; /* flag that signifies presence of a destroy cell */
+  unsigned char destroy_buf; /* flag that signifies that the buffer shuld be destroyed when the destroy cell is sent */
+
+  /* POLICY_DROP_CELLS only */
+  unsigned int win_size; /* window size for the connection (number of cells)*/
+  unsigned int win_avail; /* available window size */
+  uint32_t seq_out; /* next sequence number to use for outgoing cells */
+  uint32_t seq_in; /* next expected sequence number */
+  uint32_t ack; /* next expected ack/nack */
+  struct timeval last_ack; /* time of last ACK/NACK  */
+
+  void *prev;
+  void *next;
+} conn_buf_t;
+
+/* onion router as seen by the onion proxy */
+typedef struct 
+{
+  char *address;
+  uint32_t addr; /* address in network byte order */
+  uint16_t port; /* network port in network byte order */
+  uint16_t entry_port; /* entry port in network byte order */
+  RSA *pkey;
+  void *next;
+} routent_t;
+
+/* onion router as seen by other routers */
+typedef struct
+{
+  char *address;
+  
+  uint32_t addr;
+  uint16_t port;
+  
+  RSA *pkey; /* public RSA key */
+  /* 64-bit DES keys for link encryption */
+  char f_key[8];
+  char b_key[8];
+  char f_iv[8];
+  char b_iv[8];
+  EVP_CIPHER_CTX f_ctx;
+  EVP_CIPHER_CTX b_ctx;
+  
+  /* link info */
+  uint32_t min;
+  uint32_t max;
+  struct timeval  min_interval;
+  
+  /* time when last data was sent to that router */
+  struct timeval lastsend;
+  
+  /* socket */
+  int s;
+
+  /* connection buffers */
+  conn_buf_t *conn_bufs; /* linked list of connection buffers */
+  conn_buf_t *last_conn_buf; /* last item in the list */
+  unsigned int next_to_service; /* offset to the connection buffer that is next in turn to be serviced */
+  
+  /* cell buffer */
+  unsigned char cellbuf[128];
+  unsigned int celllen;
+  
+  void *next;
+} routentEX_t;
+
+routentEX_t *id_router(routentEX_t **routerarray, size_t rarray_len, uint32_t addr, uint16_t port);
+routentEX_t *id_routerbys(routentEX_t **routerarray, size_t rarray_len, int s);
+
+conn_buf_t *new_conn_buf(uint16_t aci, int policy, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf);
+int remove_conn_buf(conn_buf_t *conn_buf, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf);
+conn_buf_t *id_conn_buf(conn_buf_t *conn_bufs, uint16_t aci);
+
+#define __ROUTENT_H
+#endif

+ 260 - 0
src/common/scheduler.c

@@ -0,0 +1,260 @@
+/*
+ * scheduler.c
+ * Scheduler
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/04/02 10:20:37  badbytes
+ * Bug fixes.
+ *
+ * Revision 1.2  2002/03/28 10:49:07  badbytes
+ * Renamed get_trigger() to sched_trigger().
+ *
+ * Revision 1.1  2002/03/28 10:36:55  badbytes
+ * A generic scheduler.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <malloc.h>
+#include "log.h"
+#include "scheduler.h"
+
+/* create a new scheduler */
+sched_t *new_sched()
+{
+  sched_t *sched;
+  
+  sched = (sched_t *)malloc(sizeof(sched_t));
+  if (!sched)
+    return NULL;
+  
+  sched->entries = NULL;
+  return sched;
+}
+
+/* delete a scheduler from memory */
+void free_sched(sched_t *sched)
+{
+  sched_entry_t *entry;
+  if (!sched)
+    return;
+  
+  while(sched->entries)
+  {
+    entry = (sched_entry_t *)sched->entries->next;
+    free((void *)sched->entries);
+    sched->entries = entry;
+  }
+}
+
+/* add a new item to the scheduler */
+int add_sched_entry(sched_t *sched, struct timeval last, struct timeval interval)
+{
+  sched_entry_t *new_entry;
+  sched_entry_t *prev;
+  sched_entry_t *next;
+  
+  if (!sched) /* invalid parameters */
+    return -1;
+  
+  new_entry = (sched_entry_t *)malloc(sizeof(sched_entry_t));
+  if (!new_entry)
+    return -1;
+  
+  new_entry->last = last;
+  new_entry->interval = interval;
+  
+  if (!sched->entries) /* empty list */
+  {
+    sched->entries = new_entry;
+    new_entry->prev = NULL;
+    new_entry->next = NULL;
+  }
+  else /* maintain a priority queue of items */
+  {
+    /* find the next largest element in the list */
+    next = sched->entries;
+    while(next)
+    {
+      if (sched_entry_geq(next->last, next->interval, last, interval))
+      {
+	prev = (sched_entry_t *)next->prev;
+	break;
+      }
+      else
+      {
+	prev = next;
+	next = (sched_entry_t *)next->next;
+      }
+    }
+    
+    if (prev)
+      prev->next = (void *)new_entry;
+    else
+      sched->entries = new_entry;
+    
+    if (next)
+      next->prev = (void *)new_entry;
+    
+    new_entry->prev = (void *)prev;
+    new_entry->next = (void *)next;
+  }
+  
+  return 0;
+}
+
+int remove_sched_entry(sched_t *sched, struct timeval last, struct timeval interval)
+{
+  sched_entry_t *entry;
+  
+  if (!sched)
+    return -1;
+
+ if (!sched->entries)
+    return -1;
+  
+  entry = sched->entries;
+  while(entry)
+  {
+    if ((entry->last.tv_sec == last.tv_sec) && (entry->last.tv_usec = last.tv_usec) && (entry->interval.tv_sec == interval.tv_sec) && (entry->interval.tv_usec == interval.tv_usec))
+    {
+      if (entry->prev)
+	((sched_entry_t *)(entry->prev))->next = entry->next;
+      else
+	sched->entries = (sched_entry_t *)entry->next;
+      
+      if (entry->next)
+	((sched_entry_t *)(entry->next))->prev = entry->prev;
+      
+      free((void *)entry);
+      break;
+    }
+    else
+      entry = (sched_entry_t *)entry->next;
+  }
+  
+  if (entry) /* found and deleted */
+    return 0;
+  else /* not found */
+    return -1;
+}
+
+/* update an existing item with new values */
+int update_sched_entry(sched_t *sched, struct timeval old_last, struct timeval old_interval, struct timeval new_last, struct timeval new_interval)
+{
+  int retval;
+  
+  if (!sched)
+    return -1;
+  
+  /* remove the old entry first */
+  retval = remove_sched_entry(sched, old_last, old_interval);
+  if (!retval)
+  {
+    /* add the new one */
+    retval = add_sched_entry(sched, new_last, new_interval);
+  }
+  
+  return retval;
+}
+
+/* get the time interval from now until the next time an item needs to be serviced */
+int sched_trigger(sched_t *sched, struct timeval **result)
+{
+  int retval;
+  struct timeval *result_val;
+  struct timeval now;
+  struct timeval next;
+  
+  if (!sched) /* invalid parameters */
+    return -1;
+  
+  if (!sched->entries) /* no entries */
+  {
+    *result = NULL;
+    return 0;
+  }
+  
+  /* take the minimum element in the queue and calculate its next service time */
+  next.tv_sec = sched->entries->last.tv_sec + sched->entries->interval.tv_sec;
+  if (sched->entries->last.tv_usec + sched->entries->interval.tv_usec <= 999999)
+    next.tv_usec = sched->entries->last.tv_usec + sched->entries->interval.tv_usec;
+  else
+  {
+    next.tv_sec++;
+    next.tv_usec = sched->entries->last.tv_usec + sched->entries->interval.tv_usec - 1000000;
+  }
+
+  /* get current time */
+  retval = gettimeofday(&now,NULL);
+  if (retval == -1)
+    return -1;
+  
+  /* allocate memory for the result */
+  result_val = (struct timeval *)malloc(sizeof(struct timeval));
+  if (!result_val)
+    return -1;
+  
+  /* subtract now from next (return zero if negative) */
+  if ((next.tv_sec > now.tv_sec) || ((next.tv_sec == now.tv_sec) && (next.tv_usec >= now.tv_usec)))
+  {
+    result_val->tv_sec = next.tv_sec - now.tv_sec;
+    if (next.tv_usec >= now.tv_usec)
+      result_val->tv_usec = next.tv_usec - now.tv_usec;
+    else
+    {
+      result_val->tv_sec--;
+      result_val->tv_usec = 1000000 + next.tv_usec - now.tv_usec;
+    }
+  }
+  else /* next service time has already passed, return a timeout of zero */
+  {
+    result_val->tv_sec = 0;
+    result_val->tv_usec = 0;
+  }
+
+  *result = result_val;
+  
+  return 0;
+}
+
+int sched_entry_geq(struct timeval last1, struct timeval interval1, struct timeval last2, struct timeval interval2)
+{
+  struct timeval next1;
+  struct timeval next2;
+  
+  /* calculate next service time for entry1 */
+  next1.tv_sec = last1.tv_sec + interval1.tv_sec;
+  if (last1.tv_usec + interval1.tv_usec <= 999999)
+    next1.tv_usec = last1.tv_usec + interval1.tv_usec;
+  else
+  {
+    next1.tv_sec++;
+    next1.tv_usec = last1.tv_usec + interval1.tv_usec - 1000000;
+  }
+  
+  /* calculate next service time for entry2 */
+  next2.tv_sec = last2.tv_sec + interval2.tv_sec;
+  if (last2.tv_usec + interval2.tv_usec <= 999999)
+    next2.tv_usec = last2.tv_usec + interval2.tv_usec;
+  else
+  {
+    next2.tv_sec++;
+    next2.tv_usec = last2.tv_usec + interval2.tv_usec - 1000000;
+  }
+  
+  /* compare */
+  if ((next1.tv_sec > next2.tv_sec) || ((next1.tv_sec == next2.tv_sec) && (next1.tv_usec >= next2.tv_usec)))
+    return 1;
+  else
+    return 0;
+}

+ 57 - 0
src/common/scheduler.h

@@ -0,0 +1,57 @@
+/*
+ * scheduler.h
+ * Scheduler
+ * 
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.2  2002/03/28 10:49:07  badbytes
+ * Renamed get_trigger() to sched_trigger().
+ *
+ * Revision 1.1  2002/03/28 10:36:55  badbytes
+ * A generic scheduler.
+ *
+ */
+
+#ifndef __SCHEDULER_H
+
+#include <sys/time.h>
+
+typedef struct 
+{
+  struct timeval last;
+  struct timeval interval;
+  void *prev;
+  void *next;
+} sched_entry_t;
+
+typedef struct
+{
+  sched_entry_t *entries;
+} sched_t;
+
+/* create a new scheduler */
+sched_t *new_sched();
+/* delete a scheduler from memory */
+void free_sched(sched_t *sched);
+
+/* add a new item to the scheduler */
+int add_sched_entry(sched_t *sched, struct timeval last, struct timeval interval);
+/* remove an item from the scheduler */
+int remove_sched_entry(sched_t *sched, struct timeval last, struct timeval interval);
+/* update an existing item with new values */
+int update_sched_entry(sched_t *sched, struct timeval old_last, struct timeval old_interval, struct timeval new_last, struct timeval new_interval);
+
+/* get the time interval from now until the next time an item needs to be serviced */
+int sched_trigger(sched_t *sched, struct timeval **result);
+/* compare two scheduler entries (returns 1 if entry1 >= entry2, 0 otherwise */
+int sched_entry_geq(struct timeval last1, struct timeval interval1, struct timeval last2, struct timeval interval2);
+
+# define __SCHEDULER_H
+#endif

+ 63 - 0
src/common/ss.h

@@ -0,0 +1,63 @@
+/*
+ * ss.h
+ * Standard structure and related definitions.
+ * 
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.5  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.4  2002/01/26 22:45:34  mp292
+ * Added ss-related error codes.
+ *
+ * Revision 1.3  2002/01/26 19:30:09  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.1  2001/12/14 13:14:03  badbytes
+ * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies.
+ *
+ * Revision 1.2  2001/12/11 16:31:03  badbytes
+ * Changed type from ss to SS.
+ *
+ * Revision 1.1  2001/12/07 11:15:28  badbytes
+ * Added the definition for the standard structure.
+ *
+ */
+
+#ifndef __SS_H
+
+/* protocol types, as used in the standard structure */
+#define SS_PROTOCOL_TELNET 1
+#define SS_PROTOCOL_HTTP   2
+#define SS_PROTOCOL_SMTP   3
+
+/* address format types, as used in the standard structure */
+#define SS_ADDR_FMT_ASCII_HOST_PORT 1
+
+/* error codes returned by the onion proxy */
+#define SS_ERROR_SUCCESS 0
+#define SS_ERROR_VERSION_UNSUPPORTED 1
+#define SS_ERROR_ADDR_FMT_UNSUPPORTED 2
+#define SS_ERROR_INVALID_ADDRESS 3
+#define SS_ERROR_INVALID_PORT 4
+
+/* standard structure */
+typedef struct
+{
+  unsigned char version; /* version */
+  unsigned char protocol; /* protocol */
+  unsigned char retry_count; /* retry count */
+  unsigned char addr_fmt; /* address format */
+} ss_t;
+#define __SS_H
+#endif

+ 150 - 0
src/common/utils.c

@@ -0,0 +1,150 @@
+/*
+ * utils.c
+ * Miscellaneous utils.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.6  2002/03/03 00:06:45  mp292
+ * Modifications to support re-transmission.
+ *
+ * Revision 1.5  2002/01/29 02:22:41  mp292
+ * Bugfix.
+ *
+ * Revision 1.4  2002/01/29 00:58:23  mp292
+ * Timeout parametes to read_tout() and write_tout() are now pointers.
+ *
+ * Revision 1.3  2002/01/27 19:24:16  mp292
+ * Added read_tout(), write_tout() which read/write from a blocking socket but
+ * impose a timeout on the I/O operation.
+ *
+ * Revision 1.2  2002/01/26 19:30:09  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.1  2001/12/14 09:18:00  badbytes
+ * *** empty log message ***
+ *
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <stdio.h>
+
+#include "utils.h"
+#include "log.h"
+
+/* converts string to lower case */
+unsigned char *stolower(unsigned char *str)
+{
+  int i=0;
+  
+  if (str) /* valid parameters */
+  {
+    for (i=0; str[i] != 0; i++)
+      str[i] = tolower(str[i]);
+  
+    return str;
+  }
+  else return NULL;
+}
+
+/* reads data from a descriptor, just like read(), but imposes a timeout */
+/* the timeout refers to the connection being idle, not to a time limit in which the data
+ * should be received*/
+int read_tout(int s, unsigned char *buf, size_t buflen, int flags, struct timeval *conn_tout)
+{
+  int retval=0;
+  int received = 0;
+  struct timeval tout;
+  
+  fd_set mask,rmask;
+
+  FD_ZERO(&mask);
+  FD_SET(s,&mask);
+  
+  while(1)
+  {
+    rmask=mask;
+    tout = *conn_tout;
+    retval = select(s+1,&rmask,NULL,NULL,&tout);
+    if (retval == -1)
+    {
+      if (errno == EINTR)
+	continue;
+      else
+	return -1;
+    }
+    
+    if (FD_ISSET(s,&rmask))
+    {
+      retval = read(s,buf+received,buflen-received);
+      if (retval <= 0)
+	return -1;
+      else
+      {
+	received += retval;
+	if ((received < buflen) && (flags == MSG_WAITALL))
+	  continue;
+	else
+	  return received;
+      }
+    }
+    else
+      return -1;
+  }
+}
+
+/* writes data to a file descriptor, just like write(), but imposes a timeout */
+/* again this refers to the connection being idle, not a time limit in which the data should
+ * be sent */
+int write_tout(int s, unsigned char *buf, size_t buflen, struct timeval *conn_tout)
+{
+  int retval = 0;
+  int sent = 0;
+  fd_set mask,wmask;
+  struct timeval tout;
+  
+  FD_ZERO(&mask);
+  FD_SET(s,&mask);
+  
+  while(1)
+  {
+    wmask = mask;
+    tout = *conn_tout;
+    retval = select(s+1,NULL,&wmask,NULL, &tout);
+    if (retval == -1)
+    {
+      if (errno == EINTR)
+	continue;
+      else
+	return -1;
+    }
+    
+    if (FD_ISSET(s,&wmask))
+    {
+      retval = write(s,buf+sent,buflen-sent);
+      if (retval < 0)
+	return -1;
+      else
+      {
+	sent += retval;
+	if (sent < buflen)
+	  continue;
+	else
+	  return sent;
+      }
+    }
+    else
+      return -1;
+  }
+}
+

+ 53 - 0
src/common/utils.h

@@ -0,0 +1,53 @@
+/*
+ * utils.h
+ * Miscellaneous utils.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.8  2002/03/21 07:20:59  badbytes
+ * Added a dependency to <sys/time.h>.
+ *
+ * Revision 1.7  2002/03/03 00:06:45  mp292
+ * Modifications to support re-transmission.
+ *
+ * Revision 1.6  2002/01/29 02:22:41  mp292
+ * Bugfix.
+ *
+ * Revision 1.5  2002/01/29 00:58:23  mp292
+ * Timeout parametes to read_tout() and write_tout() are now pointers.
+ *
+ * Revision 1.4  2002/01/27 19:24:16  mp292
+ * Added read_tout(), write_tout() which read/write from a blocking socket but
+ * impose a timeout on the I/O operation.
+ *
+ * Revision 1.3  2002/01/26 19:30:09  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.1  2001/12/14 09:18:00  badbytes
+ * *** empty log message ***
+ *
+ */
+
+#ifndef __UTILS_H
+
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/time.h>
+
+unsigned char *stolower(unsigned char *str);
+int read_tout(int s, unsigned char *buf, size_t buflen, int flags, struct timeval *conn_tout);
+int write_tout(int s, unsigned char *buf, size_t buflen, struct timeval *conn_tout);
+
+#define __UTILS_H
+
+#endif

+ 28 - 0
src/common/version.h

@@ -0,0 +1,28 @@
+/**
+ * version.h 
+ * Protocol version.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/04/02 14:27:11  badbytes
+ * Final finishes.
+ *
+ * Revision 1.2  2001/12/18 10:37:47  badbytes
+ * Header files now only apply if they were not previously included from somewhere else.
+ *
+ * Revision 1.1  2001/12/18 09:12:05  badbytes
+ * Defines VERSION to hold the current protocol version.
+ *
+ */
+
+#ifndef __VERSION_H
+#define VERSION 2
+#define __VERSION_H
+#endif

+ 99 - 0
src/httpap/Makefile

@@ -0,0 +1,99 @@
+SRC=httpap.c http.c
+OBJ=${SRC:.c=.o}
+PROGS=httpap
+LIB=
+LIBS=
+INCLUDE =
+
+CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb
+LDFLAGS = $(LIB) $(LIBS)
+
+all:	${PROGS}
+
+clean:
+	rm -f *.o ${PROGS}
+
+depend:
+	makedepend -- ${CFLAGS} -- ${SRC}
+
+httpap:	${OBJ}
+	$(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/utils.o
+
+# DO NOT DELETE
+
+
+httpap.o: ../common/config.h ../common/log.h ../common/ss.h ../common/utils.h
+httpap.o: ../common/version.h httpap.h http.h /usr/include/alloca.h
+httpap.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h
+httpap.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h
+httpap.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h
+httpap.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+httpap.o: /usr/include/bits/environments.h /usr/include/bits/errno.h
+httpap.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h
+httpap.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h
+httpap.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+httpap.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h
+httpap.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+httpap.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h
+httpap.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h
+httpap.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h
+httpap.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h
+httpap.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h
+httpap.o: /usr/include/bits/time.h /usr/include/bits/types.h
+httpap.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h
+httpap.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+httpap.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h
+httpap.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h
+httpap.o: /usr/include/features.h /usr/include/_G_config.h
+httpap.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h
+httpap.o: /usr/include/libio.h /usr/include/limits.h
+httpap.o: /usr/include/linux/errno.h /usr/include/linux/limits.h
+httpap.o: /usr/include/netdb.h /usr/include/netinet/in.h
+httpap.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h
+httpap.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+httpap.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+httpap.o: /usr/include/sys/resource.h /usr/include/sys/select.h
+httpap.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h
+httpap.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h
+httpap.o: /usr/include/sys/types.h /usr/include/sys/ucontext.h
+httpap.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/sys/wait.h
+httpap.o: /usr/include/time.h /usr/include/ucontext.h /usr/include/unistd.h
+httpap.o: /usr/include/wait.h /usr/include/wchar.h /usr/include/xlocale.h
+httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h
+http.o: ../common/log.h ../common/utils.h http.h /usr/include/alloca.h
+http.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h
+http.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h
+http.o: /usr/include/bits/byteswap.h /usr/include/bits/confname.h
+http.o: /usr/include/bits/endian.h /usr/include/bits/environments.h
+http.o: /usr/include/bits/errno.h /usr/include/bits/in.h
+http.o: /usr/include/bits/local_lim.h /usr/include/bits/netdb.h
+http.o: /usr/include/bits/posix1_lim.h /usr/include/bits/posix2_lim.h
+http.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h
+http.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+http.o: /usr/include/bits/siginfo.h /usr/include/bits/sigset.h
+http.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h
+http.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+http.o: /usr/include/bits/types.h /usr/include/bits/uio.h
+http.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+http.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+http.o: /usr/include/bits/xopen_lim.h /usr/include/ctype.h
+http.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h
+http.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h
+http.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/limits.h
+http.o: /usr/include/linux/errno.h /usr/include/linux/limits.h
+http.o: /usr/include/netdb.h /usr/include/netinet/in.h
+http.o: /usr/include/rpc/netdb.h /usr/include/stdint.h /usr/include/stdio.h
+http.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/sys/cdefs.h
+http.o: /usr/include/syslog.h /usr/include/sys/select.h
+http.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h
+http.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h
+http.o: /usr/include/sys/types.h /usr/include/sys/uio.h /usr/include/sys/un.h
+http.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h
+http.o: /usr/include/xlocale.h
+http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h

+ 194 - 0
src/httpap/http.c

@@ -0,0 +1,194 @@
+/*
+ * http.c 
+ * HTTP parsers.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.2  2002/04/02 14:27:33  badbytes
+ * Final finishes.
+ *
+ * Revision 1.1  2002/03/12 23:46:14  mp292
+ * HTTP-related routines.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "../common/log.h"
+#include "../common/utils.h"
+
+#include "http.h"
+
+int http_get_line(int s, unsigned char **line, size_t *len, struct timeval *conn_tout)
+{
+  int retval =0; /* function return value */
+  unsigned char buf[HTTPAP_MAXLEN]; /* line buffer */
+  unsigned int buflen = 0; /* length of the received data */
+  char got_cr = 0; /* received a CR character and hence expecting a LF */
+  unsigned char c; /* input character */
+
+  if (!line || !len) /* invalid parameters */
+    return -1;
+  
+  while(1)
+  {
+    retval = read_tout(s, &c, 1, MSG_WAITALL, conn_tout);
+    if (retval < 1)
+      return -1;
+    
+    if (buflen >= HTTPAP_MAXLEN)
+      return -1;
+    
+    buf[buflen++] = c;
+    
+    if (got_cr)
+    {
+      if (c != HTTPAP_LF)
+	return -1;
+      else
+	break;
+    }
+    else
+    {
+      if (c == HTTPAP_CR)
+	got_cr = 1;
+    }
+  }
+  
+  *len = buflen;
+  if (buflen)
+  {
+    *line = (unsigned char *)malloc(buflen+1);
+    if (!*line)
+      return -1;
+    else
+    {
+      memcpy((void *)*line,(void *)buf,buflen);
+      (*line)[buflen] = 0; /* add the terminating null character */
+    }
+  }
+  else
+    *line = NULL;
+
+  return 0;
+}
+
+int http_get_version(unsigned char *rl, unsigned char **http_ver)
+{
+  unsigned char *start;
+  unsigned char *end;
+  
+  if (!rl || !http_ver) /* invalid parameters */
+    return -1;
+  
+  start = strrchr(rl, ' ');
+  if (!start)
+    return -1;
+  
+  end = strrchr(rl, HTTPAP_CR);
+  if (!end)
+    return -1;
+  
+  start++;
+  *http_ver = (unsigned char *)malloc(end-start+1);
+  if (!*http_ver)
+    return -1;
+  
+  strncpy(*http_ver, start, end-start);
+  (*http_ver)[end-start] = 0; /* terminating NULL character */
+  
+  return 0;
+}
+
+int http_get_dest(unsigned char *rl, unsigned char **addr, unsigned char **port)
+{
+  unsigned char *start;
+  unsigned char *end;
+  unsigned char *colon;
+
+  if (!rl || !addr || !port) /* invalid parameters */
+    return -1;
+  
+  start = strchr(rl, ' ');
+  if (!start)
+    return -1;
+  start++;
+  /* make sure this is really an http:// address */
+  if (strncmp(start,"http://",7))
+    return -1;
+  
+  start += 7;
+  
+  end = strchr(start,'/');
+  if (!end)
+    return -1;
+  
+  /* check for a :port in the address */
+  colon = strchr(start,':');
+  if (colon)
+  {
+    colon++;
+    *port = (unsigned char *)malloc(end-colon+1);
+    if (!*port)
+      return -1;
+    strncpy(*port,colon, end-colon);
+    (*port)[end-colon] = 0; /* terminating NULL character */
+    end = colon-1;
+  }
+  else
+    *port = NULL;
+  
+  /* extract the server address */
+  *addr = (unsigned char *)malloc(end-start+1);
+  if (!*addr)
+  {
+    if (*port)
+      free((void *)*port);
+    return -1;
+  }
+  strncpy(*addr,start, end-start);
+  (*addr)[end-start] = 0; /* terminating NULL character */
+  
+  return 0;
+}
+
+int http_get_header_name(unsigned char *rl, unsigned char **hname)
+{
+  unsigned char *end;
+  
+  if (!rl || !hname) /* invalid parameters */
+    return -1;
+  
+  end = strchr(rl, ':');
+  if (!end)
+    return -1;
+  
+  *hname = (unsigned char *)malloc(end-rl+1);
+  if (!*hname)
+    return -1;
+  
+  strncpy(*hname,rl,end-rl);
+  (*hname)[end-rl] = 0;
+  
+  return 0;
+}

+ 46 - 0
src/httpap/http.h

@@ -0,0 +1,46 @@
+/*
+ * http.h 
+ * HTTP parsers.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.2  2002/04/02 14:27:33  badbytes
+ * Final finishes.
+ *
+ * Revision 1.1  2002/03/12 23:46:14  mp292
+ * HTTP-related routines.
+ *
+ */
+
+#define HTTPAP_MAXLEN 1024 /* maximum length of a line */
+
+#define HTTPAP_CR '\015'
+#define HTTPAP_LF '\012'
+#define HTTPAP_CRLF "\015\012"
+
+#define HTTPAP_VERSION "HTTP/1.0"
+
+#define HTTPAP_STATUS_LINE_FORBIDDEN HTTPAP_VERSION " 403 Only local connections are allowed." HTTPAP_CRLF
+#define HTTPAP_STATUS_LINE_VERSION_NOT_SUPPORTED HTTPAP_VERSION " 505 Only HTTP/1.0 is supported." HTTPAP_CRLF
+#define HTTPAP_STATUS_LINE_UNAVAILABLE HTTPAP_VERSION " 503 Connection to the server failed." HTTPAP_CRLF
+#define HTTPAP_STATUS_LINE_BAD_REQUEST HTTPAP_VERSION " 400 Invalid syntax." HTTPAP_CRLF
+#define HTTPAP_STATUS_LINE_UNEXPECTED HTTPAP_VERSION " 500 Internal server error." HTTPAP_CRLF
+
+#define HTTPAP_HEADER_PROXY_CONNECTION "Proxy-Connection"
+#define HTTPAP_HEADER_USER_AGENT "User-Agent"
+#define HTTPAP_HEADER_REFERER "Referer"
+
+int http_get_line(int s, unsigned char **line, size_t *len, struct timeval *conn_tout);
+
+int http_get_version(unsigned char *rl, unsigned char **http_ver);
+
+int http_get_dest(unsigned char *rl, unsigned char **addr, unsigned char **port);
+
+int http_get_header_name(unsigned char *rl, unsigned char **hname);

+ 702 - 0
src/httpap/httpap.c

@@ -0,0 +1,702 @@
+/**
+ * httpap.c 
+ * HTTP Application Proxy for Onion Routing
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.4  2002/06/14 20:45:26  mp292
+ * Extra debugging message.
+ *
+ * Revision 1.3  2002/04/02 14:27:33  badbytes
+ * Final finishes.
+ *
+ * Revision 1.2  2002/03/12 23:40:58  mp292
+ * Tested.
+ *
+ * Revision 1.1  2002/03/11 00:21:53  mp292
+ * Coding completed. Pending testing.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "../common/log.h"
+#include "../common/config.h"
+#include "../common/ss.h"
+#include "../common/utils.h"
+#include "../common/version.h"
+
+#include "httpap.h"
+#include "http.h"
+
+int loglevel = LOG_ERR;
+struct timeval conn_tout;
+struct timeval *conn_toutp = &conn_tout;
+
+/* valid command-line options */
+static const char *args = "hf:p:l:";
+
+/* valid config file options */
+static config_opt_t options[] =
+{
+  {"OnionProxy", CONFIG_TYPE_INT, {0}, 0},
+  {"MaxConn", CONFIG_TYPE_INT, {0}, 0},
+  {"Anonimize", CONFIG_TYPE_INT, {0}, 0},
+  {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0},
+  {0}
+};
+enum opts {
+  OnionProxy=0,MaxConn, Anonimize, ConnTimeout
+};
+
+/* number of open connections */
+int connections=0;
+
+/* prints help on using httpap */
+void print_usage()
+{
+  char *program = "httpap";
+  
+  printf("\n%s - HTTP application proxy for Onion Routing.\nUsage : %s -f config [-p port -l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program);
+}
+
+/* used for reaping zombie processes */
+void sigchld_handler(int s)
+{
+  while (wait(NULL) > 0);
+  connections--;
+}
+
+int handle_connection(int new_sock, struct hostent *local, struct sockaddr_in remote, uint16_t op_port)
+{
+  int retval = 0;
+  int i;
+  char islocal = 0; /* is the accepted connection local? */
+  
+  char *cp; /* character pointer used for checking whether the connection is local */
+
+  unsigned char *line; /* one line of input */
+  int len; /* length of the line */
+  
+  unsigned char *http_ver; /* HTTP version of the incoming request */
+  unsigned char *addr; /* destination address */
+  unsigned char *port; /* destination port */
+  unsigned char *header_name; /* name of a request header */
+  
+  uint16_t portn; /* destination port converted into an integer */
+  char *errtest; /* error check when converting the port into an integer */
+  
+  ss_t ss; /* standard structure */
+  unsigned char errcode; /* error code returned by the onion proxy */
+  
+  int sop; /* socket for connecting to the onion proxy */
+  struct sockaddr_in op_addr; /* onion proxy address */
+  
+  /* for use with select() */
+  fd_set mask,rmask;
+  int maxfd;
+  
+  unsigned char buf[1024]; /* data buffer */
+  
+  log(LOG_DEBUG, "handle_connection() : Local address = %s.", inet_ntoa(*(struct in_addr *)local->h_addr));
+  log(LOG_DEBUG, "handle_connection() : Remote address = %s.", inet_ntoa(remote.sin_addr));
+  
+  /* first check that the connection is from the local host, otherwise it will be rejected */
+  if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1"))
+          islocal = 1;
+  for (i=0; (local->h_addr_list[i] != NULL) && (!islocal); i++)
+  {
+    cp = local->h_addr_list[i];
+    log(LOG_DEBUG,"handle_connection() : Checking if connection is from address %s.",inet_ntoa(*(struct in_addr *)cp));
+    if (!memcmp(&remote.sin_addr, cp, sizeof(struct in_addr)))
+      islocal = 1;
+  }
+  
+  /* bypass this check for testing purposes */
+  islocal = 1;
+  
+  /* reject a non-local connection */
+  if (!islocal)
+  {    
+    close(new_sock);
+    return 0;
+  }
+  
+  /* get the request-line */
+  retval = http_get_line(new_sock, &line, &len, conn_toutp);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection : Malformed input or connection lost.");
+    write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp);
+    close(new_sock);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection : Received this from client : %s.", line);
+  
+  /* check the HTTP version */
+  retval = http_get_version(line, &http_ver);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection : Unable to extract the HTTP version of the incoming request.");
+    write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection : Client's version is : %s.",http_ver);
+  if (strcmp(http_ver, HTTPAP_VERSION)) /* not supported */
+  {
+    log(LOG_DEBUG,"handle_connection : Client's version is %s, I only support HTTP/1.0.",http_ver);
+    write_tout(new_sock, HTTPAP_STATUS_LINE_VERSION_NOT_SUPPORTED, strlen(HTTPAP_STATUS_LINE_VERSION_NOT_SUPPORTED), conn_toutp);
+    return -1;
+  }
+  free((void *)http_ver);
+  
+  /* extract the destination address and port */
+  retval = http_get_dest(line, &addr, &port);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection : Unable to extract destination address and port number.");
+    write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp);
+    return -1;
+  }
+  if (!port) /* no destination port specified, assume the default */
+  {
+    port = (unsigned char *)malloc(6);
+    if (!port)
+    {
+      log(LOG_ERR,"Insufficient memory.");
+      write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp);
+      return -1;
+    }
+    snprintf(port,6,"%u",htons(HTTPAP_DEFAULT_HTTP_PORT));
+  }
+  else
+  {
+    log(LOG_DEBUG,"handle_connection() : Destination address is %s.",addr);
+    log(LOG_DEBUG,"handle_connection() : Destination port is %s.",port);
+  
+    /* conver the port to an integer */
+    portn = (uint16_t)strtoul(port,&errtest,0);
+    if ((*port == '\0') || (*errtest != '\0')) /* port conversion was unsuccessful */
+    {
+      log(LOG_DEBUG,"handle_connection : Unable to convert destination port.");
+      write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp);
+      return -1;
+    }
+    
+    /* convert to network order and write back to a string */
+    free((void *)port);
+    port = (unsigned char *)malloc(6);
+    if (!port)
+    {
+      log(LOG_ERR,"Insufficient memory.");
+      write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp);
+      return -1;
+    }
+    
+    snprintf(port,6,"%u",htons(portn));
+  }
+  
+  /* create a standard structure */
+  ss.version = VERSION;
+  ss.protocol = SS_PROTOCOL_HTTP;
+  ss.retry_count = 0;
+  ss.addr_fmt = SS_ADDR_FMT_ASCII_HOST_PORT;
+  
+  /* open a socket for connecting to the proxy */
+  sop = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (sop < 0)
+  {
+    log(LOG_DEBUG,"handle_connection() : Error opening socket.");
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp);
+    return -1;
+  }
+
+  log(LOG_DEBUG,"handle_connection() : Socket opened.");
+  memset((void *)&op_addr,0,sizeof(op_addr)); /* clear the structure first */
+  /* set up the sockaddr_in structure */
+  op_addr.sin_family=AF_INET;
+  op_addr.sin_port=htons(op_port);
+  memcpy((void *)&op_addr.sin_addr,local->h_addr,local->h_length);
+  log(LOG_DEBUG,"handle_connection() : Trying to connect to %s at port %u.",inet_ntoa(*((struct in_addr *)local->h_addr)),op_port);
+  
+  /* try to connect */
+  retval = connect(sop,(struct sockaddr *)&op_addr,sizeof(op_addr));
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection() : Connection to the onion proxy failed.");
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+    close(sop);
+    return -1;
+  }
+  
+  /* send the standard structure and the destination address+port */
+  retval = write_tout(sop,(unsigned char *)&ss, sizeof(ss), conn_toutp);
+  if (retval < sizeof(ss))
+  {
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+    close(sop);
+    return -1;    
+  }
+  retval = write_tout(sop,addr,strlen(addr)+1, conn_toutp);
+  if (retval < strlen(addr)+1)
+  {
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+    close(sop);
+    return -1;
+  }
+  retval = write_tout(sop,port,strlen(port)+1, conn_toutp);
+  if (retval < strlen(port)+1)
+  {
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+    close(sop);
+    return -1;
+  }
+  
+  /* wait for a return code */
+  retval = read_tout(sop, &errcode, 1, MSG_WAITALL, conn_toutp);
+  if (retval < 1)
+  {
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+    close(sop);
+    return -1;
+  }
+  
+  if (!errcode) /* onion proxy says OK */
+  {
+    /* send the request-line */
+    retval = write_tout(sop, line, strlen(line), conn_toutp);
+    if (retval < strlen(line))
+    {
+      write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+      close(new_sock);
+      return -1;
+    }
+    free((void *)line);
+    
+    /* read the request headers (if any) and sanitize if necessary */
+    while(1)
+    {
+      retval = http_get_line(new_sock, &line, &len, conn_toutp);
+      if (retval == -1)
+      {
+	log(LOG_DEBUG,"handle_connection() : Malformed input or connection lost.");
+	write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp);
+	close(new_sock);
+	return -1;
+      }
+      log(LOG_DEBUG,"handle_connection() : Received this from client : %s.", line);
+      
+      if (len == 2) /* empty line (CRLF only) signifying the end of headers  */
+      {
+	log(LOG_DEBUG,"handle_connection() : Empty line received.");
+	retval = write_tout(sop,line,strlen(line),conn_toutp);
+	if (retval < strlen(line))
+	{
+	  write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+	  close(new_sock);
+	  return -1;
+	}
+	free((void *)line);
+	break;
+      }
+      else /* process the header */
+      {
+	retval = http_get_header_name(line, &header_name);
+	if (retval == -1)
+	{
+	  log(LOG_DEBUG,"handle_connection : Unable to extract header name.");
+	  write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp);
+	  return -1;
+	}
+	log(LOG_DEBUG,"handle_connection : Identified the header as %s.", header_name);
+	
+	/* discard the Proxy-Connection header */
+	if (!strcmp(header_name,HTTPAP_HEADER_PROXY_CONNECTION))
+	  free((void *)line);
+	else if (options[Anonimize].r.i) /* did the user request anonimization? */
+	{
+	  if (!strcmp(header_name,HTTPAP_HEADER_USER_AGENT))
+	    free((void *)line);
+	  else if (!strcmp(header_name, HTTPAP_HEADER_REFERER))
+	    free((void *)line);
+	  else
+	  {
+	    retval = write_tout(sop, line, strlen(line), conn_toutp);
+	    if (retval < strlen(line))
+	    {
+	      write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+	      close(new_sock);
+	      return -1;
+	    }
+	  }
+	}
+	else
+	{
+	  retval = write_tout(sop, line, strlen(line), conn_toutp);
+	  if (retval < strlen(line))
+	  {
+	    write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp);
+	    close(new_sock);
+	    return -1;
+	  }
+	}
+	
+	free((void *)header_name);
+      }
+    }
+    
+    /* forward data in both directions until one of the principals closes it */
+    /* set up for select() */
+    log(LOG_DEBUG,"Header processed, forwarding data in both directions.");
+    FD_ZERO(&mask);
+    FD_ZERO(&rmask);
+    FD_SET(new_sock, &mask);
+    FD_SET(sop, &mask);
+    if (sop > new_sock)
+      maxfd = sop;
+    else
+      maxfd = new_sock;
+    
+    while(1)
+    {
+      rmask = mask;
+      retval = select(maxfd+1,&rmask,NULL,NULL,NULL);
+      if (retval < 0)
+      {
+	log(LOG_DEBUG,"handle_connection() : select() returned a negative integer");
+	break;
+      }
+      
+      if (FD_ISSET(sop,&rmask)) /* data from the onion proxy */
+      {
+	retval = read_tout(sop,buf,1024,0,conn_toutp);
+	if (retval <= 0)
+	{
+	  log(LOG_DEBUG,"handle_connection : Conection to the onion proxy lost.");
+	  close(sop);
+	  close(new_sock);
+	  break;
+	}
+	log(LOG_DEBUG,"handle_connection() : Received %u bytes from the onion proxy.",retval);
+	
+	retval = write_tout(new_sock, buf, retval, conn_toutp);
+	if (retval <= 0)
+	{
+	  log(LOG_DEBUG, "handle_connection : Connection to the client lost.");
+	  close(sop);
+	  close(new_sock);
+	  break;
+	}
+      }
+      
+      if (FD_ISSET(new_sock, &rmask))
+      {
+	retval = read_tout(new_sock,buf,1024,0,conn_toutp);
+	if (retval <= 0)
+	{
+	  log(LOG_DEBUG,"handle_connection : Conection to the client lost.");
+	  close(sop);
+	  close(new_sock);
+	  break;
+	}
+	log(LOG_DEBUG,"handle_connection() : Received %u bytes from the client.",retval);
+	
+	retval = write_tout(sop, buf, retval, conn_toutp);
+	if (retval <= 0)
+	{
+	  log(LOG_DEBUG, "handle_connection : Connection to the onion proxy lost.");
+	  close(sop);
+	  close(new_sock);
+	  break;
+	}
+      }
+    }
+    
+  }
+  else
+  {
+    log(LOG_DEBUG,"handle_connection() : Onion proxy returned a non-zero error code (%d)!", errcode);
+    write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp);
+    close(sop);
+    return -1;
+  }
+  
+  return 0;
+}
+
+int main(int argc, char *argv[])
+{
+  int retval = 0;
+  
+  char c; /* command-line option */
+  
+  /* configuration file */
+  char *conf_filename = NULL;
+  FILE *cf = NULL;
+  
+  struct hostent *local_host;
+  char local_hostname[512];
+  
+  struct sockaddr_in local, remote; /* local and remote address info */
+  
+  int request_sock; /* where we listen for connections */
+  int new_sock; /* for accepted connections */
+  
+  size_t sin_size; /* for accept() calls */
+  
+  u_short p; /* http proxy port */
+  u_short op_port; /* onion proxy port */
+  
+  /* used for reaping zombie processes */
+  struct sigaction sa;
+  
+  char *errtest = NULL; /* for detecting strtoul() errors */
+  
+  /* set default listening port */
+  p = htons(HTTPAP_LISTEN_PORT);
+  
+  /* deal with program arguments */
+  if ((argc < 2) && (argc > 5)) /* to few or too many arguments*/
+  {
+    print_usage();
+    return -1;
+  }
+  
+  opterr = 0;
+  while ((c = getopt(argc,argv,args)) != -1)
+  {
+    switch(c)
+    {
+     case 'f': /* config file */
+      conf_filename = optarg;
+      break;
+     case 'p':
+      p = htons((u_short)strtoul(optarg,&errtest,0));
+      if (errtest == optarg) /* error */
+      {
+	log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value.");
+	print_usage();
+	return -1;
+      }
+      break;
+     case 'h':
+      print_usage();
+      return 0;
+      break;
+     case 'l':
+      if (!strcmp(optarg,"emerg"))
+	loglevel = LOG_EMERG;
+      else if (!strcmp(optarg,"alert"))
+	loglevel = LOG_ALERT;
+      else if (!strcmp(optarg,"crit"))
+	loglevel = LOG_CRIT;
+      else if (!strcmp(optarg,"err"))
+	loglevel = LOG_ERR;
+      else if (!strcmp(optarg,"warning"))
+	loglevel = LOG_WARNING;
+      else if (!strcmp(optarg,"notice"))
+	loglevel = LOG_NOTICE;
+      else if (!strcmp(optarg,"info"))
+	loglevel = LOG_INFO;
+      else if (!strcmp(optarg,"debug"))
+	loglevel = LOG_DEBUG;
+      else
+      {
+	log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug.");
+	print_usage();
+	return -1;
+      }
+      break;
+     case '?':
+      if (isprint(c))
+	log(LOG_ERR,"Missing argument or unknown option '-%c'.",optopt);
+      else
+	log(LOG_ERR,"Unknown option character 'x%x'.",optopt);
+      print_usage();
+      return -1;
+      break;
+     default:
+      abort();
+    }
+  }
+  
+  /* the -f option is mandatory */
+  if (conf_filename == NULL)
+  {
+    log(LOG_ERR,"You must specify a config file with the -f option. See help (-h).");
+    return -1;
+  }
+  
+  /* load config file */
+  cf = open_config(conf_filename);
+  if (!cf)
+  {
+    log(LOG_ERR,"Could not open configuration file %s.",conf_filename);
+    return -1;
+  }
+  retval = parse_config(cf,options);
+  if (retval)
+    return -1;
+
+  if (options[OnionProxy].err != 1)
+  {
+    log(LOG_ERR,"The OnionProxy option is mandatory.");
+    return -1;
+  }
+  
+  if (options[MaxConn].err != 1)
+  {
+    log(LOG_ERR,"The MaxConn option is mandatory.");
+    return -1;
+  }
+  
+  if (options[Anonimize].err != 1)
+  {
+    log(LOG_ERR,"The Anonimize option is mandatory.");
+    return -1;
+  }
+  else if ((options[Anonimize].r.i != 0) && (options[Anonimize].r.i != 1))
+  {
+    log(LOG_ERR,"The Anonimize option takes the values 1 or 0.");
+    return -1;
+  }
+  
+  if (options[ConnTimeout].err != 1)
+  {
+    conn_tout.tv_sec = HTTPAP_DEFAULT_CONN_TIMEOUT;
+  }
+  else
+  {
+    if (!options[ConnTimeout].r.i)
+      conn_toutp = NULL;
+    else
+      conn_tout.tv_sec = options[ConnTimeout].r.i;
+  }
+  conn_tout.tv_usec = 0;
+  
+  op_port = (u_short)options[OnionProxy].r.i;
+  
+  /* get local address so that we know where to get the onion proxy when we need it */
+  retval = gethostname(local_hostname, (size_t)512);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Error getting local hostname");
+    return -1;
+  }
+  local_host = gethostbyname(local_hostname);
+  if (!local_host)
+  {
+    log(LOG_ERR,"Error getting local address.");
+    return -1;
+  }
+  log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname);
+  
+  /* get the server up and running */
+  request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (request_sock < 0)
+  {
+    log(LOG_ERR,"Error opening socket.");
+    return -1;
+  }
+  log(LOG_DEBUG,"Socket opened.");
+  memset((void *)&local,0,sizeof(local)); /* clear the structure first */
+  /* set up the sockaddr_in structure */
+  local.sin_family=AF_INET;
+  local.sin_addr.s_addr = INADDR_ANY;
+  local.sin_port=p;
+  /* bind it to the socket */
+  retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local));
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Error binding socket to local port %d.",ntohs(p));
+    return retval;
+  }
+  log(LOG_DEBUG,"Socket bound to port %d.",ntohs(p));
+  /* listen for connections */
+  retval = listen(request_sock,SOMAXCONN);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Could not listen for connections.");
+    return retval;
+  }
+  log(LOG_DEBUG,"Listening for connections.");
+  /* server should now be up and running */
+
+  /* install the signal handler for making sure zombie processes are killed */
+  sa.sa_handler = sigchld_handler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_RESTART;
+  retval = sigaction(SIGCHLD,&sa,NULL);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Could not install a signal handler.");
+    return -1;
+  }
+
+  /* main server loop */
+  /* I use a forking server technique - this isn't the most efficient way to do it,
+   * but it is simpler. */
+  while(1) 
+  {
+    sin_size = sizeof(struct sockaddr_in);
+    new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size);
+    if (new_sock == -1)
+    {
+      if (errno != EINTR)
+	log(LOG_ERR,"Could not accept socket connection.");
+      else
+	log(LOG_DEBUG,"Interrupt received.");
+      continue;
+    }
+    if (connections >= options[MaxConn].r.i)
+    {
+      log(LOG_NOTICE,"Number of maximum connections reached. Rejecting incoming request.");
+      close(new_sock);
+      continue;
+    }
+    
+    log(LOG_DEBUG,"Accepted a connection from %s.",inet_ntoa(remote.sin_addr));
+    connections++;
+    
+    if (!fork()) /* this is the child process */
+    {
+      close(request_sock); /* the child doesn't need the request socket anymore */
+
+      /* Main logic of httpap. */
+      retval = handle_connection(new_sock, local_host, remote, op_port);
+      /* End main logic */
+      
+      exit(retval); /* done, exit */
+    }
+    
+    close(new_sock); /* don't need this anymore */
+  }
+
+  return retval;
+
+}
+

+ 33 - 0
src/httpap/httpap.h

@@ -0,0 +1,33 @@
+/**
+ * http.h
+ * HTTP Application Proxy for Onion Routing
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/03/11 00:21:53  mp292
+ * Coding completed. Pending testing.
+ *
+ */
+
+#ifndef __HTTPAP_H
+
+#define __HTTPAP_H
+
+/* default listening port */
+#define HTTPAP_LISTEN_PORT 8080
+
+/* default SMTP port */
+#define HTTPAP_DEFAULT_HTTP_PORT 80
+
+/* default connection timeout */
+#define HTTPAP_DEFAULT_CONN_TIMEOUT 120; /* 120s */
+
+#endif
+

+ 285 - 0
src/op/Makefile

@@ -0,0 +1,285 @@
+SRC=args.c config.c op.c routers.c auth.c ss.c buffers.c crypto.c
+OBJ=${SRC:.c=.o}
+PROGS=op
+LIB=-L/usr/local/ssl/lib
+LIBS=-lcrypto
+INCLUDE = -I/usr/local/ssl/include
+
+CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb
+LDFLAGS = $(LIB) $(LIBS)
+
+all:	${PROGS}
+
+clean:
+	rm -f *.o ${PROGS}
+
+depend:
+	makedepend -- ${CFLAGS} -- ${SRC}
+
+${PROGS}:	${OBJ}
+	$(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/onion.o ../common/utils.o ../common/cell.o ../common/scheduler.o
+
+# DO NOT DELETE
+
+
+args.o: args.h ../common/log.h /usr/include/alloca.h
+args.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+args.o: /usr/include/bits/environments.h /usr/include/bits/posix_opt.h
+args.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+args.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+args.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+args.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h
+args.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+args.o: /usr/include/bits/wordsize.h /usr/include/ctype.h
+args.o: /usr/include/endian.h /usr/include/features.h /usr/include/_G_config.h
+args.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h
+args.o: /usr/include/libio.h /usr/include/stdio.h /usr/include/stdlib.h
+args.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+args.o: /usr/include/sys/select.h /usr/include/sys/syslog.h
+args.o: /usr/include/sys/sysmacros.h /usr/include/sys/types.h
+args.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h
+args.o: /usr/include/xlocale.h
+args.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+args.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+auth.o: auth.h ../common/log.h /usr/include/alloca.h /usr/include/asm/socket.h
+auth.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h
+auth.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+auth.o: /usr/include/bits/environments.h /usr/include/bits/in.h
+auth.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h
+auth.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+auth.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+auth.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+auth.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h
+auth.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+auth.o: /usr/include/bits/types.h /usr/include/bits/uio.h
+auth.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+auth.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+auth.o: /usr/include/bits/xopen_lim.h /usr/include/endian.h
+auth.o: /usr/include/features.h /usr/include/_G_config.h /usr/include/gconv.h
+auth.o: /usr/include/getopt.h /usr/include/gnu/stubs.h /usr/include/libio.h
+auth.o: /usr/include/limits.h /usr/include/linux/limits.h
+auth.o: /usr/include/netinet/in.h /usr/include/openssl/bio.h
+auth.o: /usr/include/openssl/bn.h /usr/include/openssl/crypto.h
+auth.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+auth.o: /usr/include/openssl/rand.h /usr/include/openssl/rsa.h
+auth.o: /usr/include/openssl/safestack.h /usr/include/openssl/stack.h
+auth.o: /usr/include/openssl/symhacks.h /usr/include/stdint.h
+auth.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+auth.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+auth.o: /usr/include/sys/select.h /usr/include/sys/socket.h
+auth.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+auth.o: /usr/include/sys/types.h /usr/include/sys/uio.h /usr/include/time.h
+auth.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h
+auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h
+buffers.o: buffers.h ../common/cell.h ../common/log.h ../common/onion.h
+buffers.o: ../common/routent.h ../common/version.h crypto.h op.h
+buffers.o: /usr/include/alloca.h /usr/include/bits/confname.h
+buffers.o: /usr/include/bits/endian.h /usr/include/bits/environments.h
+buffers.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h
+buffers.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+buffers.o: /usr/include/bits/sigset.h /usr/include/bits/stdio_lim.h
+buffers.o: /usr/include/bits/time.h /usr/include/bits/types.h
+buffers.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+buffers.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+buffers.o: /usr/include/endian.h /usr/include/features.h
+buffers.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h
+buffers.o: /usr/include/gnu/stubs.h /usr/include/libio.h
+buffers.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h
+buffers.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h
+buffers.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h
+buffers.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h
+buffers.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h
+buffers.o: /usr/include/openssl/evp.h /usr/include/openssl/md2.h
+buffers.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h
+buffers.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h
+buffers.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+buffers.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h
+buffers.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h
+buffers.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h
+buffers.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h
+buffers.o: /usr/include/stdint.h /usr/include/stdio.h /usr/include/stdlib.h
+buffers.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+buffers.o: /usr/include/sys/select.h /usr/include/sys/syslog.h
+buffers.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h
+buffers.o: /usr/include/sys/time.h /usr/include/sys/types.h
+buffers.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h
+buffers.o: /usr/include/xlocale.h
+buffers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+buffers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+config.o: ../common/config.h ../common/log.h config.h
+config.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+config.o: /usr/include/bits/stdio_lim.h /usr/include/bits/types.h
+config.o: /usr/include/bits/wchar.h /usr/include/features.h
+config.o: /usr/include/_G_config.h /usr/include/gconv.h
+config.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/stdio.h
+config.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+config.o: /usr/include/sys/syslog.h /usr/include/wchar.h
+config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+crypto.o: ../common/cell.h ../common/log.h ../common/onion.h
+crypto.o: ../common/routent.h ../common/version.h crypto.h
+crypto.o: /usr/include/alloca.h /usr/include/asm/errno.h
+crypto.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+crypto.o: /usr/include/bits/environments.h /usr/include/bits/errno.h
+crypto.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h
+crypto.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+crypto.o: /usr/include/bits/sigset.h /usr/include/bits/stdio_lim.h
+crypto.o: /usr/include/bits/time.h /usr/include/bits/types.h
+crypto.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h
+crypto.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h
+crypto.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h
+crypto.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h
+crypto.o: /usr/include/gnu/stubs.h /usr/include/libio.h
+crypto.o: /usr/include/linux/errno.h /usr/include/malloc.h
+crypto.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h
+crypto.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h
+crypto.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h
+crypto.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h
+crypto.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h
+crypto.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h
+crypto.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h
+crypto.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h
+crypto.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h
+crypto.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+crypto.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h
+crypto.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h
+crypto.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h
+crypto.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h
+crypto.o: /usr/include/stdint.h /usr/include/stdio.h /usr/include/stdlib.h
+crypto.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+crypto.o: /usr/include/sys/select.h /usr/include/sys/syslog.h
+crypto.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h
+crypto.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/time.h
+crypto.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h
+crypto.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+crypto.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+op.o: args.h auth.h buffers.h ../common/cell.h ../common/config.h
+op.o: ../common/log.h ../common/onion.h ../common/routent.h
+op.o: ../common/scheduler.h ../common/ss.h ../common/utils.h
+op.o: ../common/version.h config.h crypto.h op.h routers.h ss.h
+op.o: /usr/include/alloca.h /usr/include/arpa/inet.h /usr/include/asm/errno.h
+op.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h
+op.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h
+op.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+op.o: /usr/include/bits/environments.h /usr/include/bits/errno.h
+op.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h
+op.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h
+op.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+op.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h
+op.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+op.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h
+op.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h
+op.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h
+op.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h
+op.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h
+op.o: /usr/include/bits/time.h /usr/include/bits/types.h
+op.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h
+op.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+op.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h
+op.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h
+op.o: /usr/include/features.h /usr/include/_G_config.h /usr/include/gconv.h
+op.o: /usr/include/getopt.h /usr/include/gnu/stubs.h /usr/include/libio.h
+op.o: /usr/include/limits.h /usr/include/linux/errno.h
+op.o: /usr/include/linux/limits.h /usr/include/netdb.h
+op.o: /usr/include/netinet/in.h /usr/include/openssl/asn1.h
+op.o: /usr/include/openssl/bio.h /usr/include/openssl/blowfish.h
+op.o: /usr/include/openssl/bn.h /usr/include/openssl/buffer.h
+op.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h
+op.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h
+op.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h
+op.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h
+op.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h
+op.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h
+op.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h
+op.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+op.o: /usr/include/openssl/pem2.h /usr/include/openssl/pem.h
+op.o: /usr/include/openssl/pkcs7.h /usr/include/openssl/rand.h
+op.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h
+op.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h
+op.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h
+op.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h
+op.o: /usr/include/openssl/x509.h /usr/include/openssl/x509_vfy.h
+op.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h
+op.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+op.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+op.o: /usr/include/sys/resource.h /usr/include/sys/select.h
+op.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h
+op.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h
+op.o: /usr/include/sys/time.h /usr/include/sys/types.h
+op.o: /usr/include/sys/ucontext.h /usr/include/sys/uio.h /usr/include/sys/un.h
+op.o: /usr/include/sys/wait.h /usr/include/time.h /usr/include/ucontext.h
+op.o: /usr/include/unistd.h /usr/include/wait.h /usr/include/wchar.h
+op.o: /usr/include/xlocale.h
+op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h
+routers.o: ../common/cell.h ../common/config.h ../common/log.h
+routers.o: ../common/routent.h ../common/utils.h routers.h
+routers.o: /usr/include/alloca.h /usr/include/asm/errno.h
+routers.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h
+routers.o: /usr/include/bits/byteswap.h /usr/include/bits/confname.h
+routers.o: /usr/include/bits/endian.h /usr/include/bits/environments.h
+routers.o: /usr/include/bits/errno.h /usr/include/bits/in.h
+routers.o: /usr/include/bits/local_lim.h /usr/include/bits/netdb.h
+routers.o: /usr/include/bits/posix1_lim.h /usr/include/bits/posix2_lim.h
+routers.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h
+routers.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+routers.o: /usr/include/bits/siginfo.h /usr/include/bits/sigset.h
+routers.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h
+routers.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+routers.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h
+routers.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+routers.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h
+routers.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h
+routers.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h
+routers.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/limits.h
+routers.o: /usr/include/linux/errno.h /usr/include/linux/limits.h
+routers.o: /usr/include/netdb.h /usr/include/netinet/in.h
+routers.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h
+routers.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h
+routers.o: /usr/include/openssl/buffer.h /usr/include/openssl/cast.h
+routers.o: /usr/include/openssl/crypto.h /usr/include/openssl/des.h
+routers.o: /usr/include/openssl/dh.h /usr/include/openssl/dsa.h
+routers.o: /usr/include/openssl/e_os2.h /usr/include/openssl/err.h
+routers.o: /usr/include/openssl/evp.h /usr/include/openssl/lhash.h
+routers.o: /usr/include/openssl/md2.h /usr/include/openssl/md4.h
+routers.o: /usr/include/openssl/md5.h /usr/include/openssl/objects.h
+routers.o: /usr/include/openssl/obj_mac.h /usr/include/openssl/opensslconf.h
+routers.o: /usr/include/openssl/opensslv.h /usr/include/openssl/pem2.h
+routers.o: /usr/include/openssl/pem.h /usr/include/openssl/pkcs7.h
+routers.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h
+routers.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h
+routers.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h
+routers.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h
+routers.o: /usr/include/openssl/x509.h /usr/include/openssl/x509_vfy.h
+routers.o: /usr/include/rpc/netdb.h /usr/include/stdint.h /usr/include/stdio.h
+routers.o: /usr/include/stdlib.h /usr/include/string.h
+routers.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+routers.o: /usr/include/sys/select.h /usr/include/sys/syslog.h
+routers.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h
+routers.o: /usr/include/sys/time.h /usr/include/sys/types.h
+routers.o: /usr/include/sys/un.h /usr/include/time.h /usr/include/unistd.h
+routers.o: /usr/include/wchar.h /usr/include/xlocale.h
+routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h
+ss.o: ../common/log.h ../common/ss.h ../common/utils.h ../common/version.h
+ss.o: ss.h /usr/include/bits/confname.h /usr/include/bits/endian.h
+ss.o: /usr/include/bits/environments.h /usr/include/bits/posix_opt.h
+ss.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+ss.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+ss.o: /usr/include/bits/sockaddr.h /usr/include/bits/time.h
+ss.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h
+ss.o: /usr/include/endian.h /usr/include/features.h /usr/include/getopt.h
+ss.o: /usr/include/gnu/stubs.h /usr/include/malloc.h /usr/include/string.h
+ss.o: /usr/include/sys/cdefs.h /usr/include/syslog.h /usr/include/sys/select.h
+ss.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+ss.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/sys/un.h
+ss.o: /usr/include/time.h /usr/include/unistd.h /usr/include/xlocale.h
+ss.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+ss.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h

+ 121 - 0
src/op/args.c

@@ -0,0 +1,121 @@
+/**
+ * args.c 
+ * Routines for processing command-line arguments.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/01/26 22:08:40  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2001/12/14 11:26:23  badbytes
+ * Tested
+ *
+ * Revision 1.1  2001/12/13 15:15:10  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#include <unistd.h>
+#include <ctype.h>
+
+#include "../common/log.h"
+#include "args.h"
+
+/* prints help on using op */
+void print_usage()
+{
+  char *program = "op";
+  
+  printf("\n%s - Onion Proxy for Onion Routing.\nUsage : %s -f config -p port [-l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program);
+}
+
+/* get command-line arguments */
+int getargs(int argc, char *argv[], char *args, unsigned short *p, char **conf_filename, int *loglevel)
+{
+  char c; /* next option character */
+  char *errtest = NULL; /* for detecting strtoul() errors */
+  int gotf=0; int gotp=0;
+  
+  if ((!args) || (!conf_filename) || (!loglevel)) /* invalid parameters */
+    return -1;
+  
+  while ((c = getopt(argc,argv,args)) != -1)
+  {
+    switch(c)
+    {
+     case 'f': /* config file */
+      *conf_filename = optarg;
+      gotf=1;
+      break;
+     case 'p':
+      *p = (u_short)strtoul(optarg,&errtest,0);
+      if (errtest == optarg) /* error */
+      {
+	log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value. See help (-h).");
+        return -1;
+      }
+      gotp=1;
+      break;
+     case 'h':
+      print_usage();
+      exit(0);
+     case 'l':
+      if (!strcmp(optarg,"emerg"))
+	*loglevel = LOG_EMERG;
+      else if (!strcmp(optarg,"alert"))
+	*loglevel = LOG_ALERT;
+      else if (!strcmp(optarg,"crit"))
+	*loglevel = LOG_CRIT;
+      else if (!strcmp(optarg,"err"))
+	*loglevel = LOG_ERR;
+      else if (!strcmp(optarg,"warning"))
+	*loglevel = LOG_WARNING;
+      else if (!strcmp(optarg,"notice"))
+	*loglevel = LOG_NOTICE;
+      else if (!strcmp(optarg,"info"))
+	*loglevel = LOG_INFO;
+      else if (!strcmp(optarg,"debug"))
+	*loglevel = LOG_DEBUG;
+      else
+      {
+	log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug.");
+	print_usage();
+	return -1;
+      }
+      break;
+     case '?':
+      if (isprint(c))
+	log(LOG_ERR,"Missing argument or unknown option '-%c'. See help (-h).",optopt);
+      else
+	log(LOG_ERR,"Unknown option character 'x%x'. See help (-h).",optopt);
+      print_usage();
+      return -1;
+      break;
+     default:
+      return -1;
+    }
+  }
+  
+  /* the -f option is mandatory */
+  if (!gotf)
+  {
+    log(LOG_ERR,"You must specify a config file with the -f option. See help (-h).");
+    return -1;
+  }
+  
+  /* the -p option is mandatory */
+  if (!gotp)
+  {
+    log(LOG_ERR,"You must specify a port with the -p option. See help (-h).");
+    return -1;
+  }
+    
+  return 0;
+}

+ 41 - 0
src/op/args.h

@@ -0,0 +1,41 @@
+/**
+ * args.h 
+ * Routines for processing command-line arguments.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.4  2002/01/26 22:22:09  mp292
+ * Prevented duplicate definitions.
+ *
+ * Revision 1.3  2002/01/26 22:08:40  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2001/12/14 11:26:23  badbytes
+ * Tested
+ *
+ * Revision 1.1  2001/12/13 15:15:10  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#ifndef __ARGS_H
+
+#define __ARGS_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* print help */
+void print_usage();
+
+/* get command-line arguments */
+int getargs(int argc,char *argv[], char *args, unsigned short *p, char **conf_filename, int *loglevel);
+
+#endif

+ 85 - 0
src/op/auth.c

@@ -0,0 +1,85 @@
+/**
+ * auth.h
+ * Key exchange with an onion router.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/03/28 11:00:57  badbytes
+ * Key exchange with an onion router.
+ *
+ */
+#include <openssl/rand.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "../common/log.h"
+
+#include "auth.h"
+
+/* send session keys and bandwidth info to the router */
+int send_auth(int or_sock, uint32_t bandwidth, RSA *pkey, unsigned char *f_session_key, unsigned char *b_session_key)
+{
+  int retval;
+  int x;
+  unsigned char message[20]; /* bandwidth(32bits), forward key(64bits), backward key(64bits) */
+  unsigned char cipher[128];
+  if ((or_sock <= 0) || (bandwidth <= 0) || !pkey || !f_session_key || !b_session_key) /* invalid parameters */
+    return -1;
+  
+  bandwidth = htonl(bandwidth); /* convert to network order */
+  
+  /* generate the session keys */
+  retval = RAND_bytes(f_session_key, 8);
+  if (!retval)
+  {
+    log(LOG_ERR,"Not enough randomness to generate a session key.");
+    return -1;
+  }
+  retval = RAND_bytes(b_session_key, 8);
+  if (!retval)
+  {
+    log(LOG_ERR,"Not enough randomness to generate a session key.");
+    return -1;
+  }
+  
+  /* compose the message */
+  memcpy((void *)message, (void *)&bandwidth, 4);
+  memcpy((void *)(message + 4), (void *)f_session_key, 8);
+  memcpy((void *)(message + 12), (void *)b_session_key, 8);
+  printf("f_session_key: ");
+  for(x=0;x<8;x++) {
+    printf("%d ",f_session_key[x]);
+  }
+  printf("\nb_session_key: ");
+  for(x=0;x<8;x++) {
+    printf("%d ",b_session_key[x]);
+  }
+  printf("\n");
+  
+  /* encrypt with RSA */
+  retval = RSA_public_encrypt(20, message, cipher, pkey, RSA_PKCS1_PADDING);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Public key encryption failed.");
+    return -1;
+  }
+  
+  /* send the ciphertext */
+  retval = send(or_sock, cipher, 128, 0);
+  if (retval < 128)
+  {
+    log(LOG_ERR,"Connection to router lost while exchanging session keys.");
+    return -1;
+  }
+  
+  return 0;
+}

+ 23 - 0
src/op/auth.h

@@ -0,0 +1,23 @@
+/**
+ * auth.h
+ * Key exchange with an onion router.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/03/28 11:00:57  badbytes
+ * Key exchange with an onion router.
+ *
+ */
+
+#include <openssl/rsa.h>
+#include <stdint.h>
+
+/* send session keys and bandwidth info to the router */
+int send_auth(int or_sock, uint32_t bandwidth, RSA *pkey, unsigned char *f_session_key, unsigned char *b_session_key);

+ 146 - 0
src/op/buffers.c

@@ -0,0 +1,146 @@
+/**
+ * buffers.c
+ * Buffers.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ */
+
+
+#include <unistd.h>
+#include <openssl/evp.h>
+
+#include "../common/cell.h"
+#include "../common/log.h"
+
+#include "buffers.h"
+#include "crypto.h"
+#include "op.h"
+
+int buffer_data(uint16_t aci, unsigned char *buf, size_t buflen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen)
+{
+  int retval;
+  int i;
+  cell_t *cellbuf;
+  cell_t *c;
+  size_t cellbuflen;
+  size_t cells;
+  unsigned char *tmpbuf; /* temporary buffer for realloc() operations */
+  
+  if (!buf || !outbuf || !outbuflen) /* invalid parameters */
+    return -1;
+  
+  /* split the plaintext into DATA cells */
+  retval = pack_data(aci,buf, buflen, (unsigned char **)&cellbuf, &cellbuflen);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"buffer_data() : Could not pack data into cells.");
+    return -1;
+  }
+  log(LOG_DEBUG,"buffer_data() : DATA cells created.");
+  
+  cells = cellbuflen/(sizeof(cell_t));
+  /* encrypt the cells */
+  for (i=0; i<cells; i++)
+  {
+    c = cellbuf+i;
+    /* encrypt the payload length */
+    retval = crypt_f((unsigned char *)&c->length, 1, cpath, cpathlen);
+    if (retval == -1)
+    {
+      log(LOG_ERR,"Could not encrypt the payload length of a DATA cell.");
+      free((void *)cellbuf);
+      return -1;
+    }
+    /* encrypt the payload */
+    retval = crypt_f((unsigned char *)c->payload, CELL_PAYLOAD_SIZE, cpath, cpathlen);
+    if (retval == -1)
+    {
+      log(LOG_ERR,"Could not encrypt the payload of a DATA cell.");
+      free((void *)cellbuf);
+      return -1;
+    }
+  }
+
+  /* now copy the cells into the output buffer */
+  if (*outbuflen-*outbuf_dataoffset-*outbuf_datalen < cellbuflen) /* increase the buffer size if necessary */
+  {
+    /* allocate a new buffer (in OP_DEFAULT_BUFSIZE chunks)*/
+    tmpbuf = (unsigned char *)malloc(((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1)*OP_DEFAULT_BUFSIZE);
+    if (!tmpbuf)
+    {
+      log(LOG_ERR,"Error allocating memory.");
+      free((void *)cellbuf);
+      return -1;
+    }
+    /* copy old data to the new buffer */
+    memcpy((void *)tmpbuf,(void *)(*outbuf+*outbuf_dataoffset),*outbuf_datalen);
+    /* replace the old buffer with the new one */
+    if (*outbuf)
+      free((void *)*outbuf);
+    *outbuf = tmpbuf;
+    *outbuflen = ((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1) * OP_DEFAULT_BUFSIZE;
+    *outbuf_dataoffset = 0;
+  }
+  memcpy((void *)(*outbuf + *outbuf_dataoffset + *outbuf_datalen), (void *)cellbuf, cellbuflen);
+  *outbuf_datalen += cellbuflen;
+  
+  return 0;
+}
+
+int buffer_create(uint16_t aci, unsigned char *onion, size_t onionlen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen)
+{
+  int retval;
+  cell_t *cellbuf;
+  size_t cells;
+  size_t cellbuflen;
+  unsigned char *tmpbuf; /* temporary buffer for realloc() operations */
+  
+  if (!onion || !outbuf || !outbuflen || !outbuf_dataoffset || !outbuf_datalen) /* invalid parameters */
+    return -1;
+  
+  retval = pack_create(aci,onion, onionlen, (unsigned char **)&cellbuf, &cellbuflen);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"buffer_create() : Could not pack the onion into cells.");
+    return -1;
+  }
+  log(LOG_DEBUG,"buffer_create() : CREATE cells created.");
+
+  cells = cellbuflen/(sizeof(cell_t));
+
+  /* now copy the cells into the output buffer */
+  if (*outbuflen-*outbuf_dataoffset-*outbuf_datalen < cellbuflen) /* increase the buffer size if necessary */
+  {
+    /* allocate a new buffer (in OP_DEFAULT_BUFSIZE chunks)*/
+    tmpbuf = (unsigned char *)malloc(((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1)*OP_DEFAULT_BUFSIZE);
+    if (!tmpbuf)
+    {
+      log(LOG_ERR,"Error allocating memory.");
+      free((void *)cellbuf);
+      return -1;
+    }
+    /* copy old data to the new buffer */
+    memcpy((void *)tmpbuf,(void *)(*outbuf+*outbuf_dataoffset),*outbuf_datalen);
+    /* replace the old buffer with the new one */
+    if (*outbuf)
+      free((void *)*outbuf);
+    *outbuf = tmpbuf;
+    *outbuflen = ((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1) * OP_DEFAULT_BUFSIZE;
+    *outbuf_dataoffset = 0;
+  }
+  memcpy((void *)(*outbuf + *outbuf_dataoffset + *outbuf_datalen), (void *)cellbuf, cellbuflen);
+  *outbuf_datalen += cellbuflen;
+  
+  return 0;
+}

+ 25 - 0
src/op/buffers.h

@@ -0,0 +1,25 @@
+/**
+ * buffers.h
+ * Buffers.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ */
+
+
+#include <stdint.h>
+
+#include "../common/onion.h"
+
+int buffer_data(uint16_t aci, unsigned char *buf, size_t buflen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen);
+int buffer_create(uint16_t aci, unsigned char *onion, size_t onionlen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen);

+ 49 - 0
src/op/config.c

@@ -0,0 +1,49 @@
+/**
+ * config.c 
+ * Routines for loading the configuration file.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ * Revision 1.2  2002/01/26 22:09:53  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.1  2001/12/13 15:15:10  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#include "config.h"
+#include "../common/log.h"
+
+/* loads the configuration file */
+int getconfig(char *conf_filename, config_opt_t *options)
+{
+  FILE *cf = NULL;
+  int retval = 0;
+
+  if ((!conf_filename) || (!options))
+    return -1;
+  
+  /* load config file */
+  cf = open_config(conf_filename);
+  if (!cf)
+  {
+    log(LOG_ERR,"Could not open configuration file %s.",conf_filename);
+    return -1;
+  }
+  retval = parse_config(cf,options);
+  if (retval)
+    return -1;
+
+  return 0;
+}

+ 30 - 0
src/op/config.h

@@ -0,0 +1,30 @@
+/**
+ * config.h 
+ * Routines for loading the configuration file.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.4  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ * Revision 1.3  2002/01/26 22:55:11  mp292
+ * *** empty log message ***
+ *
+ * Revision 1.2  2002/01/26 22:22:09  mp292
+ * Prevented duplicate definitions.
+ *
+ * Revision 1.1  2001/12/13 15:15:10  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+#include "../common/config.h"
+
+/* loads the configuration file */
+int getconfig(char *filename, config_opt_t *options);

+ 104 - 0
src/op/crypto.c

@@ -0,0 +1,104 @@
+/**
+ * crypto.c
+ * Crypto calls.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ */
+
+#include <malloc.h>
+#include <unistd.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "../common/log.h"
+
+#include "crypto.h"
+
+int crypt_f(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen)
+{
+  int i=0;
+  int retval = 0;
+  unsigned char *ciphertext = NULL;
+  crypt_path_t *thishop;
+  
+  /* allocate the ciphertext buffer */
+  ciphertext = (unsigned char *)malloc(buflen);
+  if (!ciphertext)
+  {
+    log(LOG_ERR,"Error allocating memory.");
+    return -1;
+  }
+  
+  for (i=0; i < cpathlen; i++) /* moving from last to first hop 
+				* Remember : cpath is in reverse order, i.e. last hop first
+				*/
+  {
+    log(LOG_DEBUG,"crypt_f() : Processing hop %u",cpathlen-i);
+    thishop = cpath[i];
+    
+    /* encrypt */
+    retval = EVP_EncryptUpdate(&thishop->f_ctx,ciphertext, &buflen, buf, buflen);
+    if (!retval) /* error */
+    {
+      log(LOG_ERR,"Error performing encryption:%s",ERR_reason_error_string(ERR_get_error()));
+      free(ciphertext);
+      return -1;
+    }
+    
+    /* copy ciphertext back to buf */
+    memcpy((void *)buf,(void *)ciphertext,buflen);
+  }
+  free((void *)ciphertext);
+
+  return 0;
+}
+ 
+int crypt_b(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen)
+{
+  int i=0;
+  int retval=0;
+  unsigned char *plaintext=NULL;
+  crypt_path_t *thishop;
+
+  /* allocate the plaintext buffer */
+  plaintext = (unsigned char *)malloc(buflen);
+  if (!plaintext)
+  {
+    log(LOG_ERR,"Error allocating memory.");
+    return -1;
+  }
+  
+  for (i=cpathlen-1; i >= 0; i--) /* moving from first to last hop 
+				* Remember : cpath is in reverse order, i.e. last hop first
+				*/
+  {
+    thishop = cpath[i];
+    
+    /* encrypt */
+    retval = EVP_DecryptUpdate(&thishop->b_ctx,plaintext, &buflen, buf, buflen);
+    if (!retval) /* error */
+    {
+      log(LOG_ERR,"Error performing decryption:%s",ERR_reason_error_string(ERR_get_error()));
+      free(plaintext);
+      return -1;
+    }
+    
+    /* copy plaintext back to buf */
+    memcpy((void *)buf,(void *)plaintext,buflen);
+  }
+  
+  free(plaintext);
+  
+  return 0;
+}

+ 23 - 0
src/op/crypto.h

@@ -0,0 +1,23 @@
+/**
+ * crypto.h
+ * Crypto calls.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ */
+
+#include "../common/onion.h"
+
+int crypt_f(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen);
+
+int crypt_b(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen);

+ 916 - 0
src/op/op.c

@@ -0,0 +1,916 @@
+/**
+ * op.c 
+ * Onion Proxy
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.37  2002/06/14 20:45:56  mp292
+ * *** empty log message ***
+ *
+ * Revision 1.36  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ * Revision 1.35  2002/04/02 10:21:07  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.34  2002/03/29 08:35:12  badbytes
+ * Link encryption is now done on the entire cell header for simplicity.
+ *
+ * Revision 1.33  2002/03/28 17:57:59  badbytes
+ * Bug fix.
+ *
+ * Revision 1.32  2002/03/28 11:01:43  badbytes
+ * Now does link-encryption and link-padding.
+ *
+ * Revision 1.31  2002/03/12 23:40:32  mp292
+ * Started on op<->router connection padding.
+ *
+ * Revision 1.30  2002/01/29 02:22:58  mp292
+ * Put a timeout on all network I/O.
+ *
+ * Revision 1.29  2002/01/26 23:01:55  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.28  2002/01/18 20:42:06  mp292
+ * Reflects changes to common/onion.c:new_route()
+ *
+ * Revision 1.27  2002/01/17 23:49:15  mp292
+ * Added size of public key to one of the debugging messages.
+ *
+ * Revision 1.26  2002/01/16 23:01:58  mp292
+ * First phase of system testing completed (main functionality).
+ *
+ * Revision 1.25  2002/01/16 17:01:56  mp292
+ * There was a bug in checking whether the incoming connection is local or not.
+ *
+ * Revision 1.24  2002/01/16 16:09:32  mp292
+ * A pointer cast was missing. Fixed.
+ *
+ * Revision 1.23  2002/01/14 13:05:39  badbytes
+ * System testing in progress.
+ *
+ * Revision 1.22  2002/01/11 15:47:25  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.21  2002/01/09 09:18:35  badbytes
+ * Now handles EINTR error from accept().
+ *
+ * Revision 1.20  2002/01/09 07:57:18  badbytes
+ * Ciphers got out of sync, hopefully fixed.
+ *
+ * Revision 1.19  2001/12/19 11:15:41  badbytes
+ * Corrected AF_INET to PF_INET in socket() calls.
+ *
+ * Revision 1.18  2001/12/19 08:38:38  badbytes
+ * Zombie problems hopefully fixed.
+ *
+ * Revision 1.17  2001/12/19 08:29:29  badbytes
+ * Tested. Still some problems with zombies in both op and smtpap.
+ *
+ * Revision 1.16  2001/12/18 15:51:58  badbytes
+ * Connection with onion router established. Will continue testing tomorrow.
+ *
+ * Revision 1.15  2001/12/18 14:12:05  badbytes
+ * Tested up to connect() to onion router.
+ *
+ * Revision 1.14  2001/12/18 12:21:11  badbytes
+ * Forgot to convert port to network order :-)
+ *
+ * Revision 1.13  2001/12/18 11:52:27  badbytes
+ * Coding completed. Proceeding to test.
+ *
+ * Revision 1.12  2001/12/17 13:36:15  badbytes
+ * Writing handle_connection()
+ *
+ * Revision 1.11  2001/12/17 08:42:44  badbytes
+ * getrouters() now returns an array of routers and also writes the length of the array to an int*.
+ *
+ * Revision 1.10  2001/12/14 14:45:13  badbytes
+ * Added range checking for CoinWeight.
+ *
+ * Revision 1.9  2001/12/14 14:08:50  badbytes
+ * getrouters() now returns an array of pointers rather than a linked list
+ *
+ * Revision 1.8  2001/12/14 13:31:20  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.7  2001/12/14 13:17:12  badbytes
+ * Corrected references to types.h
+ *
+ * Revision 1.6  2001/12/14 13:00:30  badbytes
+ * Changed my mind, routers.c and routers.h stay where they are :-)
+ *
+ * Revision 1.5  2001/12/14 12:56:55  badbytes
+ * Moved routers* to common/
+ *
+ * Revision 1.4  2001/12/14 12:42:50  badbytes
+ * References to onion.h and onion.o now point to the common/ directory.
+ *
+ * Revision 1.3  2001/12/14 12:40:26  badbytes
+ * Was being stupid - op doesn't need a private key!! Have removed ...
+ *
+ * Revision 1.2  2001/12/14 11:27:16  badbytes
+ * Configuration and server setup completed.
+ *
+ * Revision 1.1  2001/12/13 15:15:11  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include "../common/log.h"
+#include "../common/version.h"
+#include "../common/onion.h"
+#include "../common/utils.h"
+#include "../common/cell.h"
+#include "../common/scheduler.h"
+
+#include "config.h"
+#include "routers.h"
+#include "args.h"
+#include "auth.h"
+#include "op.h"
+#include "ss.h"
+#include "crypto.h"
+#include "buffers.h"
+
+/* global variables */
+
+/* default logging threshold */
+int loglevel = LOG_ERR;
+struct timeval conn_tout;
+struct timeval *conn_toutp = &conn_tout; 
+
+/* valid command-line options */
+static char *args = "hf:p:l:";
+
+/* valid config file options */
+static config_opt_t options[] =
+{
+  {"RouterFile", CONFIG_TYPE_STRING, {0}, 0},
+  {"CoinWeight", CONFIG_TYPE_DOUBLE, {0}, 0},
+  {"MaxConn", CONFIG_TYPE_INT, {0}, 0},
+  {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0},
+  {"Bandwidth", CONFIG_TYPE_INT, {0}, 0},
+  {0}
+};
+enum opts {
+  RouterFile=0, CoinWeight, MaxConn, ConnTimeout, Bandwidth
+};
+
+int connections = 0; /* number of active connections */
+
+/* local host info */
+struct hostent *local_host;
+char local_hostname[512];
+  
+struct sockaddr_in local, remote; /* local and remote address info */
+struct sockaddr_in or_addr; /* onion router address */
+
+int request_sock; /* where we listen for connections */
+int new_sock; /* for accepted connections */
+int or_sock; /* for connecting to the first onion router */
+
+/* router array */
+routent_t **routerarray = NULL;
+int rarray_len = 0;
+
+/* end of global variables */
+
+void send_to_router(int s,unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, struct timeval *lastsend, struct timeval *interval, sched_t *scheduler, EVP_CIPHER_CTX *ctx)
+{
+  int retval;
+  int cells;
+  int datacells;
+  int paddingcells;
+  int i;
+  int x;
+  char *px;
+  struct timeval now;
+  cell_t cipher;
+  cell_t *padding;
+  int cipherlen;
+  unsigned long elapsed;
+  
+  /* calculate the number of cells that need to be sent */
+  retval = gettimeofday(&now,NULL);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Could not get current time!");
+    return;
+  }
+  
+  elapsed = 1000000*(now.tv_sec-lastsend->tv_sec) + now.tv_usec-lastsend->tv_usec;
+  
+  if (elapsed < 1000000)
+  {
+    cells = ((options[Bandwidth].r.i) * 512) / /* number of bytes per second, divided by two */
+      (1000000/elapsed);  /* fractions of second since last send */
+  }
+  else
+  {
+    cells = ((options[Bandwidth].r.i) * 512) * /* number of bytes per second, divided by two */
+      (elapsed/1000000); /* 1/fractions of second since last send */
+  }
+  cells /= sizeof(cell_t);
+
+  datacells = (*outbuf_datalen)/sizeof(cell_t); /* number of data cells available */
+  if (datacells > cells)
+    datacells = cells;
+  paddingcells = cells - datacells;
+  
+  /* send the data cells first */
+  for (i=0; i<datacells; i++)
+  {
+    /* link-encrypt the cell header */
+    printf("Cell header plaintext: ");
+    for(x=0;x<8;x++) {
+      printf("%u ",*(char *)(*outbuf+*outbuf_dataoffset+x));
+    }
+    printf("\n");
+    retval = EVP_EncryptUpdate(ctx, (unsigned char *)&cipher, &cipherlen, *outbuf+*outbuf_dataoffset, 8);
+    if (!retval)
+    {
+      log(LOG_ERR,"Link encryption failed. Exiting.");
+      exit(-1);
+    }
+    printf("Cell header crypttext: ");
+    px = (char *)&cipher;
+    for(x=0;x<8;x++) {
+      printf("%u ",px[x]);
+    }
+    printf("\n");
+
+    /* copy the payload */
+    memcpy((void *)cipher.payload, (void *)(*outbuf+*outbuf_dataoffset+8), CELL_PAYLOAD_SIZE);
+    
+    /* send the cell */
+    log(LOG_DEBUG,"send_to_router(): Trying to send a data/create cell to router.");
+    retval = write_tout(s,(unsigned char *)&cipher, sizeof(cell_t), conn_toutp);
+    if (retval < sizeof(cell_t))
+    {
+      log(LOG_ERR,"Connection to the router seems to be lost. Exiting.");
+      exit(-1);
+    }
+    *outbuf_dataoffset += sizeof(cell_t);
+    *outbuf_datalen -= sizeof(cell_t);
+    
+  }
+  
+  /* send padding */
+  for (i=0; i<cells-datacells; i++)
+  {
+    padding = new_padding_cell();
+    if (!padding)
+    {
+      log(LOG_ERR,"Memory allocation error. Exiting.");
+      exit(-1);
+    }
+    
+    /* link encrypt the cell header */
+    retval = EVP_EncryptUpdate(ctx, (unsigned char *)&cipher, &cipherlen, (unsigned char *)padding, 8);
+    if (!retval)
+    {
+      log(LOG_ERR,"Link encryption failed. Exiting.");
+      exit(-1);
+    }
+    
+    /* copy the payload */
+    memcpy((void *)cipher.payload, (void *)((unsigned char *)padding+8), CELL_PAYLOAD_SIZE);
+    
+    /* send the cell */
+    log(LOG_DEBUG,"send_to_router(): Trying to send a padding cell to router.");
+    retval = write_tout(s, (unsigned char *)&cipher, sizeof(cell_t), conn_toutp);
+    if (retval < sizeof(cell_t))
+    {
+      log(LOG_ERR,"Connection to the router seems to be lost. Exiting.");
+      exit(-1);
+    }
+    
+    free((void *)padding);
+  }
+  
+  /* update scheduler state, if we've sent anything to the router */
+  if (cells)
+  {
+    retval = update_sched_entry(scheduler, *lastsend, *interval, now, *interval);
+    if (retval == -1)
+    {
+      log(LOG_ERR,"Scheduler error. Exiting.");
+      exit(-1);
+    }
+    memcpy((void *)lastsend,(void *)&now, sizeof(struct timeval));
+  }
+  
+}
+
+/* deal with a client */
+int handle_connection()
+{
+  int retval = 0;
+  int routelen = 0; /* length of the route */
+  unsigned int *route = NULL; /* hops in the route as an array of indexes into rarray */
+  routent_t *firsthop = NULL;
+  
+  uint32_t aci; /* ACI for this connection */
+  
+  unsigned char *onion = NULL; /* holds the onion */
+  int onionlen = 0; /* onion length in host order */
+
+  crypt_path_t **cpath = NULL; /* defines the crypt operations that need to be performed on incoming/outgoing data */
+  char *dest_addr = NULL; /* destination address in ASCII format */
+  
+  int dest_addrlen = 0;
+  char *dest_port = NULL; /* destination port in ASCII format */
+  int dest_portlen = 0;
+  ss_t *ss; /* standard structure */
+  
+  uint32_t router_addr_net; /* address of the first onion router in network order */
+  
+  unsigned char inbuf[1024]; /* buffer for forwarding data between ap and or */
+  
+  unsigned char *outbuf = NULL; /* buffer for cells which are to be transmitted to the first core onion router in the route */
+  size_t outbuflen = 0;
+  size_t outbuf_dataoffset = 0; /* offset to the beginning of the data */
+  size_t outbuf_datalen = 0; /* length of the data stored in the buffer */
+  
+  cell_t cellbuf;
+  int cellbuflen = 0;
+  
+  struct timeval lastsend; /* time of last transmission to the onion router */
+  struct timeval interval; /* transmission interval */
+  
+  /* link encryption */
+  unsigned char f_session_key[8];
+  unsigned char f_session_iv[8] = {0,0,0,0,0,0,0,0};
+  unsigned char b_session_key[8];
+  unsigned char b_session_iv[8] = {0,0,0,0,0,0,0,0};
+  EVP_CIPHER_CTX f_ctx;
+  EVP_CIPHER_CTX b_ctx;
+  
+  /* scheduler */
+  sched_t *scheduler;
+  
+  /* for use with select() */
+  fd_set rmask, mask;
+  int maxfd;
+  struct timeval *timeout;
+  
+  /* get the standard structure */
+  retval = process_ss(new_sock, conn_toutp, &ss,&dest_addr, &dest_addrlen, &dest_port, &dest_portlen);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Error processing the standard structure.");
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Destination = %s:%s",dest_addr,dest_port);
+  
+  /* choose a route */
+  route = (unsigned int *)new_route(options[CoinWeight].r.d, routerarray,rarray_len, &routelen);
+  if (!route)
+  {
+    log(LOG_ERR,"Error choosing a route through the OR network.");
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Chosen a route of length %u : ",routelen);
+  for (retval=routelen-1;retval>=0;retval--)
+  {
+    log(LOG_DEBUG,"handle_connection() : %u : %s:%u, %u",routelen-retval,(routerarray[route[retval]])->address,ntohs((routerarray[route[retval]])->port),RSA_size((routerarray[route[retval]])->pkey));
+  }
+  
+  /* allocate memory for the crypt path */
+  cpath = malloc(routelen * sizeof(crypt_path_t *));
+  if (!cpath)
+  {
+    log(LOG_ERR,"Error allocating memory.");
+    free(route);
+    return -1;
+  }
+  /* create an onion and calculate crypto keys */
+  onion = create_onion(routerarray,rarray_len,route,routelen,&onionlen,cpath);
+  if (!onion)
+  {
+    log(LOG_ERR,"Error creating an onion.");
+    free(route);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Created an onion of size %u bytes.",onionlen);
+  log(LOG_DEBUG,"handle_connection() : Crypt path :");
+  for (retval=0;retval<routelen;retval++)
+  {
+    log(LOG_DEBUG,"handle_connection() : %u/%u",(cpath[retval])->forwf, (cpath[retval])->backf);
+  }
+  
+  /* connect to first onion router */
+  or_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (or_sock < 0)
+  {
+    free(route);
+    free(onion);
+    free(cpath);
+    close(new_sock);
+    log(LOG_ERR,"Error creating socket.");
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Socket created.");
+
+  firsthop = routerarray[route[routelen-1]];
+  memset((void *)&or_addr,0,sizeof(or_addr));
+  or_addr.sin_family=AF_INET;
+  or_addr.sin_port=firsthop->entry_port;
+  router_addr_net = firsthop->addr;
+  memcpy(&or_addr.sin_addr,&router_addr_net,sizeof(struct sockaddr_in));
+  log(LOG_DEBUG,"handle_connection() : Trying to connect to %s:%u",inet_ntoa(or_addr.sin_addr), ntohs(or_addr.sin_port));
+  retval = connect(or_sock,(struct sockaddr *)&or_addr, sizeof(or_addr));
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Could not connect to onion router.");
+    free(route);
+    free(onion);
+    free(cpath);
+    close(or_sock);
+    close(new_sock);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Connected to first onion router.");
+
+  /* send session key and bandwidth info */
+  retval = send_auth(or_sock, options[Bandwidth].r.i, firsthop->pkey, f_session_key, b_session_key);
+  if (retval == -1)
+  {
+    close(or_sock);
+    close(new_sock);
+    log(LOG_ERR,"Lost connection to an onion router. Exiting.");
+    return -1;
+  }
+  /* initialize crypto engines */
+  EVP_CIPHER_CTX_init(&f_ctx);
+  EVP_CIPHER_CTX_init(&b_ctx);
+  EVP_EncryptInit(&f_ctx, EVP_des_ofb(), f_session_key, f_session_iv);
+  EVP_DecryptInit(&b_ctx, EVP_des_ofb(), b_session_key, b_session_iv);
+  
+  /* chose an ACI */
+  do
+  {
+    retval = RAND_pseudo_bytes((unsigned char *)&aci, 2);
+    if (retval==-1)
+    {
+      log(LOG_ERR,"Random data generator doesn't seem to work. Exiting.");
+      return -1;
+    }
+  } while(!aci); /* don't allow zero ACIs */
+  log(LOG_DEBUG,"handle_connection() : ACI %u chosen.",aci);
+  
+  /* initialize last time of transmission to now */
+  retval = gettimeofday(&lastsend, NULL);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Could not get current time.");
+    return -1;
+  }
+  /* calculate the transmission interval */
+  interval.tv_sec = 0;
+  interval.tv_usec = 250000/options[Bandwidth].r.i;
+  /* initialize the scheduler */
+  scheduler = new_sched();
+  if (!scheduler)
+  {
+    log(LOG_ERR,"Could not initialize scheduler.");
+    return -1;
+  }
+  retval = add_sched_entry(scheduler, lastsend, interval);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Could not initialize scheduler.");
+    return -1;
+  }
+  timeout = NULL;
+  
+  /* write the onion into the output buffer */
+  retval = buffer_create(aci, (unsigned char *)onion, onionlen, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection() : Could not buffer the onion.");
+    close(or_sock);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Onion buffered for output.");
+
+  /* send standard structure */
+  log(LOG_DEBUG,"handle_connection() : Calling send_crypt ... routelen=%u, sizeof(SS) = %u",routelen,sizeof(ss_t));
+  retval = buffer_data(aci, (unsigned char *)ss, sizeof(ss_t), &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection() : Could not buffer the standard structure for output.");
+    close(or_sock);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Buffered the standard structure header.");
+  retval = buffer_data(aci, dest_addr,dest_addrlen, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection() : Could not buffer the standard structure (dest. address) for output.");
+    close(or_sock);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Buffered the destination address.");
+  retval = buffer_data(aci, dest_port, dest_portlen, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen);
+  if (retval == -1)
+  {
+    log(LOG_DEBUG,"handle_connection() : Could not buffer the standard structure (dest. port) for output.");
+    close(or_sock);
+    return -1;
+  }
+  log(LOG_DEBUG,"handle_connection() : Buffered the destination port.");
+
+  
+  /* forward data in both directions, crypt as necessary */
+  /* use select() */
+
+  FD_ZERO(&mask);
+  FD_SET(new_sock, &mask);
+  FD_SET(or_sock, &mask);
+  if (new_sock > or_sock)
+    maxfd = new_sock;
+  else
+    maxfd = or_sock;
+
+  while(1)
+  {
+    rmask = mask;
+    
+    /* delete old timeout */
+    if (timeout)
+      free((void *)timeout);
+    /* get the new one */
+    retval = sched_trigger(scheduler, &timeout);
+    if (retval == -1)
+    {
+      log(LOG_DEBUG,"Scheduler error.");
+      break;
+    }
+    retval = select(maxfd+1,&rmask,NULL,NULL,timeout);
+    if (retval < 0)
+    {
+      log(LOG_DEBUG,"handle_connection() : select() returned negative integer");
+      break;
+    }
+    
+    if (FD_ISSET(new_sock,&rmask))
+    {
+      log(LOG_DEBUG,"handle_connection() : FD_ISSET(new_sock)");
+      retval = read_tout(new_sock, inbuf, 1024, 0, conn_toutp);
+      if (retval <= 0)
+      {
+	log(LOG_DEBUG,"handle_connection() : Received EOF on new_sock.");
+	break;
+      }
+      log(LOG_DEBUG,"handle_connection() : Received %u bytes from client.",retval);
+      retval = buffer_data(aci, inbuf, retval, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen);
+     
+      if (retval < 0)
+      {
+	log(LOG_DEBUG,"handle_connection() : Could not buffer data for output to OR.");
+	break;
+      }
+      log(LOG_DEBUG,"handle_connection() : Buffered %u bytes for output to the OR.",retval);
+    }
+    
+    if (FD_ISSET(or_sock, &rmask))
+    {
+      log(LOG_DEBUG,"handle_connection() : FD_ISSET(or_sock)");
+      /* read the remainder of the cell (or whatever we can get) */
+      retval = read_tout(or_sock, ((unsigned char *)&cellbuf)+cellbuflen, sizeof(cell_t) - cellbuflen, 0, conn_toutp);
+      if (retval <= 0)
+      {
+	log(LOG_DEBUG,"handle_connection() : Received EOF on or_sock.");
+	break;
+      }
+      log(LOG_DEBUG,"handle_connection() : Received %u bytes from router.",retval);
+      cellbuflen += retval;
+      
+      if (cellbuflen == sizeof(cell_t)) /* received an entire cell */
+      {
+	/* link decrypt the cell header */
+	retval = EVP_DecryptUpdate(&b_ctx, (unsigned char *)inbuf, &cellbuflen, (unsigned char *)&cellbuf, 8);
+	if (!retval)
+	{
+	  log(LOG_ERR,"Decryption error. Closing the connection and exiting.");
+	  break;
+	}
+	
+	if (((cell_t *)inbuf)->command == CELL_PADDING) /* padding, discard */
+	{
+	  log(LOG_DEBUG,"Received a PADDING cell. Discarding.");
+	  ; /* discard */
+	}
+	else if (((cell_t *)inbuf)->command == CELL_DATA) /* only process DATA cells , discard otherwise */
+	{
+	  /* decrypt the payload length */
+	  retval = crypt_b((unsigned char *)&((cell_t *)inbuf)->length, 1, cpath, routelen);
+	  if (retval == -1)
+	  {
+	    log(LOG_ERR,"Decryption error. Closing the connection and exiting.");
+	    break;
+	  }
+	  
+	  /* decrypt the payload */
+	  retval = crypt_b((unsigned char *)cellbuf.payload, CELL_PAYLOAD_SIZE, cpath, routelen);
+	  if (retval == -1)
+	  {
+	    log(LOG_ERR,"Decryption error. Closing the connection and exiting.");
+	    break;
+	  }
+	  
+	  /* send the payload to the application proxy */
+	  retval = write_tout(new_sock, (unsigned char *)cellbuf.payload, ((cell_t *)inbuf)->length, conn_toutp);
+	  if (retval < ((cell_t *)inbuf)->length)
+	  {
+	    log(LOG_ERR,"Connection to the application proxy seems to be lost.");
+	    break;
+	  }
+	  log(LOG_DEBUG,"handle_connection() : Sent %u bytes to client.",retval);
+	}
+	else
+	  log(LOG_DEBUG,"handle_connection() : Recived cell has incorrect command or ACI. Discarding.");
+	
+	cellbuflen = 0; /* get ready for the next cell */
+      }
+    }
+
+    /* send cells to the router */
+    send_to_router(or_sock,&outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, &lastsend, &interval, scheduler, &f_ctx);
+  }
+  
+  /* clean up */
+  log(LOG_DEBUG,"handle_connection() : handle_connection() exiting.");
+  close(or_sock);
+  
+  return 0;
+}
+
+/* used for reaping zombie processes */
+void sigchld_handler(int s)
+{
+  while (wait(NULL) > 0);
+  connections--;
+}
+
+int main(int argc, char *argv[])
+{
+  int one = 1;
+  int retval = 0;
+  
+  char *cp; /* temporary storage */
+  int i=0; /* iteration counter */
+  
+  char *conf_filename = NULL; /* configuration file */
+
+  size_t sin_size; /* for accept() calls */
+  
+  u_short p; /* onion proxy port */
+  
+  /* used for reaping zombie processes */
+  struct sigaction sa;
+
+  int islocal = 0; /* is the incoming connection local? */
+
+  struct rlimit cd_limit; /* resource limit to prevent core dumps */
+  
+  /* prevent core dump */
+  retval = getrlimit(RLIMIT_CORE, &cd_limit);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Could not tell the OS to prevent core dumps for the process.");
+    return -1;
+  }
+  cd_limit.rlim_cur = 0;
+  retval = setrlimit(RLIMIT_CORE, &cd_limit);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Could not tell the OS to prevent core dumps for the process.");
+    return -1;
+  }
+  
+  /* get command-line arguments */
+  retval = getargs(argc,argv,args,&p,&conf_filename,&loglevel);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Error processing command-line arguments.");
+    exit(1);
+  }
+  
+  /* load config file */
+  retval = getconfig(conf_filename,options);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Error loading configuration file.");
+    exit(1);
+  }
+  
+  if (options[RouterFile].err != 1)
+  {
+    log(LOG_ERR,"RouterFile option required, but not found.");
+    exit(1);
+  }
+  
+  if (options[CoinWeight].err == -1)
+  {
+    log(LOG_ERR,"Error reading the CoinWeight option.");
+    exit(1);
+  }
+  
+  if (options[CoinWeight].err == 0)
+  {
+    /* this is optional, so if not found, set default value */
+    options[CoinWeight].r.d = OP_DEFAULT_COIN_WEIGHT;
+  }
+  else if ((options[CoinWeight].r.d < 0) || (options[CoinWeight].r.d >= 1))
+  {
+    /* must be a value in [0,1) */
+    log(LOG_ERR,"CoinWeight option must be >= 0 and < 1.");
+    exit(1);
+  }
+  
+  if (options[Bandwidth].err == 0)
+  {
+    /* optional, set to default */
+    options[Bandwidth].r.i = OP_DEFAULT_BANDWIDTH;
+  }
+  else if (options[Bandwidth].r.i <= 0)
+  {
+    log(LOG_ERR,"The Bandwidth option must be an integer greater than zero.");
+    exit(1);
+  }
+  
+  if (options[ConnTimeout].err != 1)
+  {
+    conn_tout.tv_sec = OP_DEFAULT_CONN_TIMEOUT;
+    conn_tout.tv_usec = 0;
+  }
+  else
+  {
+    if (!options[ConnTimeout].r.i)
+      conn_toutp = NULL;
+    else
+      conn_tout.tv_sec = options[ConnTimeout].r.i;
+    conn_tout.tv_usec = 0;
+  }
+  
+  /* load the routers file */
+  routerarray = getrouters(options[RouterFile].r.str,&rarray_len);
+  if (!routerarray)
+  {
+    log(LOG_ERR,"Error loading router list.");
+    exit(1);
+  }
+  
+  /* get local address so that we know where to allow connections from*/
+  retval = gethostname(local_hostname, (size_t)512);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Error getting local hostname.");
+    return -1;
+  }
+  local_host = gethostbyname(local_hostname);
+  if (!local_host)
+  {
+    log(LOG_ERR,"Error getting local address.");
+    return -1;
+  }
+  log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname);
+  
+  /* get the server up and running */
+  request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (request_sock < 0)
+  {
+    log(LOG_ERR,"Error opening socket.");
+    return -1;
+  }
+  setsockopt(request_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+  log(LOG_DEBUG,"main() : Socket opened.");
+  
+  memset((void *)&local,0,sizeof(local)); /* clear the structure first */
+  /* set up the sockaddr_in structure */
+  local.sin_family=AF_INET;
+  local.sin_addr.s_addr = INADDR_ANY;
+  local.sin_port=htons(p);
+  /* bind it to the socket */
+  retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local));
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Error binding socket to local port %d.",p);
+    return retval;
+  }
+  log(LOG_DEBUG,"main() : Socket bound to port %d.",p);
+  /* listen for connections */
+  retval = listen(request_sock,SOMAXCONN);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Could not listen for connections.");
+    return retval;
+  }
+  log(LOG_DEBUG,"main() : Listening for connections.");
+  /* server should now be up and running */
+
+  /* install the signal handler for making sure zombie processes are killed */
+  sa.sa_handler = sigchld_handler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_RESTART;
+  retval = sigaction(SIGCHLD,&sa,NULL);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Could not install a signal handler.");
+    return -1;
+  }
+
+  /* main server loop */
+  /* I use a forking server technique - this isn't the most efficient way to do it,
+   * but it is simpler. */
+  while(1)
+  {
+    sin_size = sizeof(struct sockaddr_in);
+    new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size);
+    if (new_sock == -1)
+    {
+      if (errno != EINTR)
+	log(LOG_ERR,"Could not accept socket connection.");
+      else
+	log(LOG_DEBUG,"main() : Interrupt received.");
+      continue;
+    }
+    
+    if (connections == options[MaxConn].r.i)
+    {
+      close(new_sock);
+      log(LOG_NOTICE,"Maximum connection limit exceeded. Rejecting incoming request.");
+    }
+    connections++;
+    log(LOG_DEBUG,"main() : Accepted a connection from %s.",inet_ntoa(remote.sin_addr));
+    
+    /* see if the connection is local, otherwise reject */
+    /* first check that the connection is from the local host, otherwise reject */
+    if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1"))
+      islocal=1;
+    for (i=0; (local_host->h_addr_list[i] != NULL) && (!islocal); i++)
+    {
+      cp = local_host->h_addr_list[i];
+      if (!memcmp(&remote.sin_addr, cp,sizeof(struct in_addr)))
+	islocal = 1;
+    }
+    
+    if (!islocal)
+    {
+      log(LOG_DEBUG,"main() : Incoming connection is not local. Will reject.");
+      close(new_sock);
+    }
+    else
+    {
+      log(LOG_DEBUG,"main() : Incoming connection seems to be local. Will accept.");
+      /* fork a process to deal with the customer */
+      if (!fork()) /* this is the child process */
+      { 
+	close(request_sock); /* the child doesn't need the request socket anymore */
+	
+	/* Main logic of op. */
+	retval = handle_connection();
+	log(LOG_DEBUG,"main() : Handle connection returned %d.",retval);
+	/* End main logic */
+	
+	exit(retval); /* done, exit */
+      } 
+    
+      close(new_sock); /* don't need this anymore */
+    }
+  }
+
+  return retval;
+
+}
+

+ 54 - 0
src/op/op.h

@@ -0,0 +1,54 @@
+/**
+ * op.h
+ * Onion Proxy
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.7  2002/03/28 11:01:43  badbytes
+ * Now does link-encryption and link-padding.
+ *
+ * Revision 1.6  2002/03/12 23:40:32  mp292
+ * Started on op<->router connection padding.
+ *
+ * Revision 1.5  2002/01/29 02:22:58  mp292
+ * Put a timeout on all network I/O.
+ *
+ * Revision 1.4  2002/01/26 23:01:55  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.3  2001/12/18 11:52:27  badbytes
+ * Coding completed. Proceeding to test.
+ *
+ * Revision 1.2  2001/12/17 13:36:15  badbytes
+ * Writing handle_connection()
+ *
+ * Revision 1.1  2001/12/13 15:15:11  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#ifndef __OP_H
+
+#define __OP_H
+
+/* choosing the length of a route uses a weighted coin
+ * this is the default value for it */
+#define OP_DEFAULT_COIN_WEIGHT 0.8
+
+/* default connection timeout */
+#define OP_DEFAULT_CONN_TIMEOUT 120 /* 120s */
+
+/* default connection bandwidth */
+#define OP_DEFAULT_BANDWIDTH 1 /* 1kb/s */
+
+/* default buffer size per connection */
+#define OP_DEFAULT_BUFSIZE 4096 /* 4kb */
+
+#endif

+ 364 - 0
src/op/routers.c

@@ -0,0 +1,364 @@
+/**
+ * routers.c 
+ * Routines for loading the list of routers and their public RSA keys.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.16  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ * Revision 1.15  2002/03/25 10:48:48  badbytes
+ * Added explicit dependency on <netinet/in.h>.
+ *
+ * Revision 1.14  2002/01/27 19:24:33  mp292
+ * Fixed a bug in parameter checking.
+ *
+ * Revision 1.13  2002/01/26 22:19:15  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.12  2002/01/18 20:42:25  mp292
+ * Slight modification to the way keys are read from the route file.
+ *
+ * Revision 1.11  2002/01/14 13:05:39  badbytes
+ * System testing in progress.
+ *
+ * Revision 1.10  2002/01/11 15:47:25  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.9  2001/12/18 15:51:58  badbytes
+ * Connection with onion router established. Will continue testing tomorrow.
+ *
+ * Revision 1.8  2001/12/17 13:36:15  badbytes
+ * Writing handle_connection()
+ *
+ * Revision 1.7  2001/12/17 08:42:45  badbytes
+ * getrouters() now returns an array of routers and also writes the length of the array to an int*.
+ *
+ * Revision 1.6  2001/12/14 14:08:50  badbytes
+ * getrouters() now returns an array of pointers rather than a linked list
+ *
+ * Revision 1.5  2001/12/14 14:05:56  badbytes
+ * Added routent_t** make_rarray(routent_t* list);
+ *
+ * Revision 1.4  2001/12/14 13:25:17  badbytes
+ * Moved back from common/
+ *
+ * Revision 1.2  2001/12/14 11:24:57  badbytes
+ * Tested.
+ *
+ * Revision 1.1  2001/12/13 15:15:11  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "routers.h"
+#include "../common/log.h"
+#include "../common/utils.h"
+#include "../common/config.h"
+
+/* delete a list of routers from memory */
+void delete_routerlist(routent_t *list)
+{
+  routent_t *tmp = NULL;
+  
+  if (!list)
+    return;
+  
+  do
+  {
+    tmp=list->next;
+    free(list->address);
+    RSA_free(list->pkey);
+    free(list);
+    list = tmp;
+  }
+  while (list != NULL);
+  
+  return;
+}
+
+/* create an NULL-terminated array of pointers pointing to elements of a router list */
+/* this is done in two passes through the list - inefficient but irrelevant as this is
+ * only done once when op/or start up */
+routent_t **make_rarray(routent_t* list, size_t *len)
+{
+  routent_t *tmp=NULL;
+  int listlen = 0;
+  routent_t **array=NULL;
+  routent_t **p=NULL;
+  
+  if ((!list) || (!len))
+    return NULL;
+  
+  /* get the length of the list */
+  tmp = list;
+  do
+  {
+    listlen++;
+    tmp = tmp->next;
+  }
+  while (tmp != NULL);
+  
+  array = malloc((listlen+1)*sizeof(routent_t *));
+  if (!array)
+  {
+    log(LOG_ERR,"Error allocating memory.");
+    return NULL;
+  }
+  
+  tmp=list;
+  p = array;
+  do
+  {
+    *p = tmp;
+    p++;
+    tmp = tmp->next;
+  }
+  while(tmp != NULL);
+  *p=NULL;
+  
+  *len = listlen;
+  return array;
+}
+
+/* load the router list */
+routent_t **getrouters(char *routerfile, size_t *lenp)
+{
+  int retval = 0;
+  char *retp = NULL;
+  routent_t *router=NULL, *routerlist=NULL, *lastrouter=NULL;
+  FILE *rf; /* router file */
+  fpos_t fpos;
+  char line[512];
+  char *token;
+  char *errtest; /* detecting errors in strtoul() calls */
+  struct hostent *rent;
+  
+  if ((!routerfile) || (!lenp)) /* invalid parameters */
+    return NULL;
+
+  if (strspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(routerfile)) /* invalid filename */
+  {
+    log(LOG_ERR,"Could not open %s because it contains illegal characters.",routerfile);
+    return NULL;
+  }
+  
+  /* open the router list */
+  rf = fopen(routerfile,"r");
+  if (!rf)
+  {
+    log(LOG_ERR,"Could not open %s.",routerfile);
+    return NULL;
+  }
+  
+  retp= fgets(line,512,rf);
+  while (retp)
+  {
+    log(LOG_DEBUG,"getrouters() : Line :%s",line);
+    token = (char *)strtok(line,OP_ROUTERLIST_SEPCHARS);
+    if (token)
+    {
+      log(LOG_DEBUG,"getrouters() : Token : %s",token);
+      if (token[0] != '#') /* ignore comment lines */
+      {
+	router = malloc(sizeof(routent_t));
+	if (!router)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  fclose(rf);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+	
+	/* read the address */
+	router->address = malloc(strlen(token)+1);
+	if (!router->address)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  fclose(rf);
+	  free(router);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+	strcpy(router->address,token);
+	
+	rent = (struct hostent *)gethostbyname(router->address);
+	if (!rent)
+	{
+	  log(LOG_ERR,"Could not get address for router %s.",router->address);
+	  fclose(rf);
+	  free(router->address);
+	  free(router);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+
+	memcpy(&router->addr, rent->h_addr,rent->h_length);
+	
+	/* read the network port */
+	token = (char *)strtok(NULL,OP_ROUTERLIST_SEPCHARS);
+	if (token) /* network port */
+	{
+	  log(LOG_DEBUG,"getrouters() : Token :%s",token);
+	  router->port = (uint16_t)strtoul(token,&errtest,0);
+	  if ((*token != '\0') && (*errtest == '\0')) /* network port conversion was successful */
+	  {
+	    router->port = htons(router->port);
+	    /* read the entry port */
+	    token = (char *)strtok(NULL,OP_ROUTERLIST_SEPCHARS);
+	    if (token) /* entry port */
+	    {
+	      log(LOG_DEBUG,"getrouters() : Token :%s",token);
+	      router->entry_port = (uint16_t)strtoul(token,&errtest,0);
+	      if ((*token != '\0') && (*errtest == '\0')) /* entry port number conversion was successful */
+	      {
+		router->entry_port = htons(router->entry_port);
+		/* check that there is a public key entry for that router */
+		retval = fgetpos(rf, &fpos); /* save the current file position
+					      * we wil return to it later if we find a public key */
+		if (retval == -1)
+		{
+		  log(LOG_ERR,"Could not save position in %s.",routerfile);
+		  free(router->address);
+		  free(router);
+		  fclose(rf);
+		  delete_routerlist(routerlist);
+		  return NULL;
+		}
+		do /* read through to the next non-empty line */
+		{
+		  retp=fgets(line,512,rf);
+		  if (!retp)
+		  {
+		    log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
+		    free(router->address);
+		    free(router);
+		    fclose(rf);
+		    delete_routerlist(routerlist);
+		    return NULL;
+		  }
+		  log(LOG_DEBUG,"getrouters() : Line:%s",line);
+		  if ((*line != '#') && (strspn(line,OP_ROUTERLIST_SEPCHARS) != strlen(line) ))
+		  {
+		    break;
+		  }
+		} while (1);
+	    
+		if (!strcmp(line,OP_PUBLICKEY_BEGIN_TAG)) /* we've got the public key */
+		{
+		  retval = fsetpos(rf,&fpos); /* get us back to where we were otherwise crypto lib won't find the key */
+		  if (retval == -1)
+		  {
+		    log(LOG_ERR,"Could not set position in %s.",routerfile);
+		    free(router->address);
+		    free(router);
+		    fclose(rf);
+		    delete_routerlist(routerlist);
+		    return NULL;
+		  }
+		}
+		else /* we found something else; this isn't right */
+		{
+		  log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
+		  free(router->address);
+		  free(router);
+		  fclose(rf);
+		  delete_routerlist(routerlist);
+		  return NULL;
+		}
+		
+		log(LOG_DEBUG,"getrouters() : Reading the key ...");
+		/* read the public key into router->pkey */
+		router->pkey=NULL;
+		router->pkey = PEM_read_RSAPublicKey(rf,NULL,NULL,NULL);
+		if (!router->pkey) /* something went wrong */
+		{
+		  log(LOG_ERR,"Could not read public key for router %s:%u.",router->address,router->port);
+		  free(router->address);
+		  free(router);
+		  fclose(rf);
+		  delete_routerlist(routerlist);
+		  return NULL;
+		}
+		else /* read the key */
+		{
+		  log(LOG_DEBUG,"getrouters() : Public key size = %u.", RSA_size(router->pkey));
+		  if (RSA_size(router->pkey) != 128) /* keys MUST be 1024 bits in size */
+		  {
+		    log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->port);
+		    free(router->address);
+		    RSA_free(router->pkey);
+		    free(router);
+		    fclose(rf);
+		    delete_routerlist(routerlist);
+		    return NULL;
+		  }
+		  router->next = NULL;
+		  /* save the entry into the routerlist linked list */
+		  if (!routerlist) /* this is the first entry */
+		    routerlist = router;
+		  else
+		    lastrouter->next = (void *)router;
+		  lastrouter = router;
+		}
+	      }
+	      else
+	      {
+		log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid entry funnel port.",router->address);
+		free(router->address);
+		free(router);
+		fclose(rf);
+		delete_routerlist(routerlist);
+		return NULL;
+	      }
+	    }
+	    else
+	    {
+	      log(LOG_ERR,"Entry for router %s doesn't seem to contain an entry funnel port.",router->address);
+	      free(router->address);
+	      free(router);
+	      fclose(rf);
+	      delete_routerlist(routerlist);
+	      return NULL;
+	    }
+	  }
+	  else
+	  {
+	    log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid network funnel port.",router->address);
+	    free(router->address);
+	    free(router);
+	    fclose(rf);
+	    delete_routerlist(routerlist);
+	    return NULL;
+	  }
+	}
+	else
+	{
+	  log(LOG_ERR,"Entry for router %s doesn't seem to contain a network funnel port.",router->address);
+	  free(router->address);
+	  free(router);
+	  fclose(rf);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+      }
+    }
+    retp=fgets(line,512,rf);
+  }
+  
+  fclose(rf);
+  return make_rarray(routerlist, lenp);
+}

+ 66 - 0
src/op/routers.h

@@ -0,0 +1,66 @@
+/**
+ * routers.h
+ * Routines for loading the list of routers and their public RSA keys.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.11  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ * Revision 1.10  2002/01/26 22:22:09  mp292
+ * Prevented duplicate definitions.
+ *
+ * Revision 1.9  2002/01/26 22:19:15  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.8  2001/12/17 08:42:45  badbytes
+ * getrouters() now returns an array of routers and also writes the length of the array to an int*.
+ *
+ * Revision 1.7  2001/12/14 14:08:50  badbytes
+ * getrouters() now returns an array of pointers rather than a linked list
+ *
+ * Revision 1.6  2001/12/14 14:05:56  badbytes
+ * Added routent** make_rarray(routent_t* list);
+ *
+ * Revision 1.5  2001/12/14 13:32:18  badbytes
+ * No longer contains the definition of routent_t. This is now in common/routent_t.h
+ *
+ * Revision 1.4  2001/12/14 13:25:17  badbytes
+ * Moved back from common/
+ *
+ * Revision 1.2  2001/12/14 11:24:57  badbytes
+ * Tested.
+ *
+ * Revision 1.1  2001/12/13 15:15:11  badbytes
+ * Started coding the onion proxy.
+ *
+ */
+
+#ifndef __ROUTERS_H
+
+#define __ROUTERS_H
+
+#include <openssl/rsa.h>
+#include "../common/routent.h"
+
+#define OP_ROUTERLIST_SEPCHARS " \t\n"
+
+#define OP_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n"
+
+/* load the list of routers into memory */
+routent_t **getrouters(char *routerfile, size_t *listlenp);
+
+/* free the router list pointed to by list */
+void delete_routerlist(routent_t *list);
+
+/* create an NULL-terminated array of pointers pointing to elements of a router list */
+routent_t **make_rarray(routent_t* list, size_t *listlenp);
+
+#endif

+ 194 - 0
src/op/ss.c

@@ -0,0 +1,194 @@
+/**
+ * ss.c
+ * Standard structure processing.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ */
+
+
+#include <malloc.h>
+#include <unistd.h>
+
+#include "../common/log.h"
+#include "../common/version.h"
+#include "../common/utils.h"
+
+#include "ss.h"
+
+/* read the standard structure, check if it's acceptable and send an appropriate error code
+ * Returns : 
+ *   -1 processing error
+ *    0 OK
+ *    1 no error, but standard structure rejected
+ */
+int process_ss(int s, struct timeval *conn_toutp, ss_t **ssp, char **addrp, int *addrlenp, char **portp, int *portlenp)
+{
+  int retval = 0;
+  int len = 0; /* number of bytes read */
+  ss_t *ss; /* standard structure */
+  char errcode = SS_ERROR_SUCCESS; /* error code which we send back to the client */
+  char inbuf;
+  char *addr = NULL; /* destination address */
+  int addrlen = 0;
+  char *port = NULL; /* destination port */
+  int portlen = 0;
+  char *tmp = NULL; /* temporary storage */
+  
+  if ((!ssp) || (!addrp) || (!addrlenp) || (!portp) || (!portlenp)) /* invalid parameters */
+    return -1;
+  
+  /* allocate memory for SS */
+  ss = malloc(sizeof(ss_t));
+  if (!ss)
+  {
+    log(LOG_ERR,"Error allocating memory.");
+    return -1;
+  }
+  
+  log(LOG_DEBUG,"Allocated memory for ss.");
+  
+  len = 0;
+  while (len < sizeof(ss_t)) /* need to make sure the entire ss is read */
+  {
+    retval = read_tout(s,(char *)ss+len,sizeof(ss_t)-len,0, conn_toutp);
+    if (retval <= 0)
+    {
+      free(ss);
+      log(LOG_ERR,"Could not receive standard structure.");
+      return -1;
+    }
+    len +=retval;
+  }
+  
+  if ((ss->version == 0) || (ss->version != VERSION)) /* unsupported version */
+  {
+    log(LOG_DEBUG,"Unsupported version.");
+    free(ss);
+    errcode = SS_ERROR_VERSION_UNSUPPORTED;
+    write_tout(s,&errcode,1,conn_toutp);
+    return -1;
+  }
+  
+  if (ss->addr_fmt != SS_ADDR_FMT_ASCII_HOST_PORT) /* unrecognized address format */
+  {
+    log(LOG_DEBUG,"Unrecognized address format.");
+    free(ss);
+    errcode = SS_ERROR_ADDR_FMT_UNSUPPORTED;
+    write_tout(s,&errcode,1,conn_toutp);
+    return -1;
+  }
+  
+  /* allocate memory for the destination address - 512 bytes maximum */
+  addrlen=512;
+  addr = malloc(addrlen);
+  if (!addr)
+  {
+    free(ss);
+    log(LOG_ERR,"Error allocating memory.");
+    return -1;
+  }
+  
+  /* now read the destination address */
+  len = 0;
+  do /* need to keep going until the entire string is read in */
+  {
+    if (len == addrlen) /* we've run out of space, abort */
+    {
+      free(ss);
+      free(addr);
+      log(LOG_ERR,"Client tried to send address > 512 characters.");
+      errcode = SS_ERROR_INVALID_ADDRESS;
+      write_tout(s,&errcode,1,conn_toutp);
+      return -1;
+    }
+    retval = read_tout(s,(void *)&inbuf, 1, 0, conn_toutp);
+    if (retval <= 0)
+    {
+      free(ss);
+      free(addr);
+      log(LOG_ERR,"Error receiving destination address.");
+      return -1;
+    }
+    *(addr+len) = inbuf;
+    len++;
+  } while (inbuf != 0);
+
+  
+  /* allocate memory for the destination port - 6 bytes maximum */
+  portlen = 6;
+  port = malloc(portlen);
+  if (!port)
+  {
+    free(ss);
+    log(LOG_ERR,"Error allocating memory.");
+    free(addr);
+    return -1;
+  }
+  /* now read the destination port */
+  len = 0;
+  do /* keep going until the entire string is read in */
+  {
+    if (len == portlen) /* no more space, abort */
+    {
+      free(ss);
+      free(addr);
+      free(port);
+      log(LOG_ERR,"Client tried to send port > 6 characters.");
+      errcode = SS_ERROR_INVALID_PORT;
+      write_tout(s,&errcode,1,conn_toutp);
+      return -1;
+    }
+    retval = read_tout(s,(void *)&inbuf, 1, 0, conn_toutp);
+    if (retval <= 0)
+    {
+      free(ss);
+      free(addr);
+      free(port);
+      log(LOG_ERR,"Error receiving destination port.");
+      return -1;
+    }
+    *(port+len)=inbuf;
+    len++;
+  } while (inbuf != 0);
+
+  /* send a success error code back to the client */
+  errcode = SS_ERROR_SUCCESS;
+  write_tout(s,&errcode,1,conn_toutp);
+  
+  /* done, now save */
+  addrlen = strlen(addr)+1;
+  tmp = addr;
+  addr = realloc(addr,addrlen);
+  /* if realloc() fails, we just ignore it and use the previously allocated memory, although this may be wasteful */
+  if (!addr)
+    addr=tmp; /* restore previous state */
+  else
+    addr[addrlen-1]=0;
+  
+  portlen = strlen(port)+1;
+  tmp=port;
+  port = realloc(port,portlen);
+  if (!port)
+    port=tmp;
+  else
+    port[portlen-1]=0;
+  
+  *ssp = ss;
+  *addrp = addr;
+  *addrlenp = addrlen;
+  *portp = port;
+  *portlenp = portlen;
+  
+  return 0;
+}

+ 22 - 0
src/op/ss.h

@@ -0,0 +1,22 @@
+/**
+ * ss.h
+ * Standard structure processing.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/04/02 14:28:01  badbytes
+ * Final finishes.
+ *
+ */
+
+
+#include "../common/ss.h"
+
+int process_ss(int s, struct timeval *conn_toutp, ss_t **ssp, char **addrp, int *addrlenp, char **portp, int *portlenp);

+ 18 - 0
src/or/Makefile

@@ -0,0 +1,18 @@
+SRC=args.c buffers.c cell.c circuit.c command.c connection.c connection_app.c connection_op.c connection_or.c config.c main.c onion.c routers.c
+OBJ=${SRC:.c=.o}
+PROGS=or
+LIB=
+LIBS=
+INCLUDE = -I/usr/local/ssl/include
+
+CFLAGS=  $(INCLUDE) -Wall -Wpointer-arith -ggdb
+LDFLAGS = $(LIB) $(LIBS)
+
+all:	${OBJ} ${PROGS}
+
+or:	${OBJ}
+	gcc -o or $(CFLAGS) *.o ../common/*.o -lcrypto
+
+clean:
+	rm -f *.o ${PROGS}
+

+ 99 - 0
src/or/args.c

@@ -0,0 +1,99 @@
+/**
+ * args.c 
+ * Routines for processing command-line arguments.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/01/27 00:42:50  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.2  2002/01/04 10:05:28  badbytes
+ * Completed.
+ *
+ * Revision 1.1  2002/01/03 10:23:43  badbytes
+ * Code based on that in op. Needs to be modified.
+ */
+
+#include "or.h"
+
+/* prints help on using or */
+void print_usage()
+{
+  char *program = "or";
+  printf("\n%s - Onion Router.\nUsage : %s -f config [-l loglevel -h]\n-h : display this help\n-f config : config file\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program);
+}
+
+
+/* get command-line arguments */
+int getargs(int argc,char *argv[], char *args, char **conf_filename, int *loglevel)
+{
+  char c; /* next option character */
+  int gotf=0;
+
+  if ((!args) || (!conf_filename) || (!loglevel))
+    return -1;
+  
+  while ((c = getopt(argc,argv,args)) != -1)
+  {
+    switch(c)
+    {
+     case 'f': /* config file */
+      *conf_filename = optarg;
+      gotf=1;
+      break;
+     case 'h':
+      print_usage(argv[0]);
+      exit(0);
+     case 'l':
+      if (!strcmp(optarg,"emerg"))
+	*loglevel = LOG_EMERG;
+      else if (!strcmp(optarg,"alert"))
+	*loglevel = LOG_ALERT;
+      else if (!strcmp(optarg,"crit"))
+	*loglevel = LOG_CRIT;
+      else if (!strcmp(optarg,"err"))
+	*loglevel = LOG_ERR;
+      else if (!strcmp(optarg,"warning"))
+	*loglevel = LOG_WARNING;
+      else if (!strcmp(optarg,"notice"))
+	*loglevel = LOG_NOTICE;
+      else if (!strcmp(optarg,"info"))
+	*loglevel = LOG_INFO;
+      else if (!strcmp(optarg,"debug"))
+	*loglevel = LOG_DEBUG;
+      else
+      {
+	log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug.");
+	print_usage(argv[0]);
+	return -1;
+      }
+      break;
+     case '?':
+      if (isprint(c))
+	log(LOG_ERR,"Missing argument or unknown option '-%c'. See help (-h).",optopt);
+      else
+	log(LOG_ERR,"Unknown option character 'x%x'. See help (-h).",optopt);
+      print_usage(argv[0]);
+      return -1;
+      break;
+     default:
+      return -1;
+    }
+  }
+  
+  /* the -f option is mandatory */
+  if (!gotf)
+  {
+    log(LOG_ERR,"You must specify a config file with the -f option. See help (-h).");
+    return -1;
+  }
+  
+  return 0;
+}

+ 174 - 0
src/or/buffers.c

@@ -0,0 +1,174 @@
+
+/* buffers.c */
+
+#include "or.h"
+
+int buf_new(char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+  if (!pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+    return -1;
+
+  *pbuf = (char *)malloc(MAX_BUF_SIZE);
+  if(!*pbuf)
+    return -1;
+  memset(*pbuf,0,MAX_BUF_SIZE);
+  *pbuflen = MAX_BUF_SIZE;
+  *pbuf_datalen = 0;
+
+  return 0;
+}
+
+int buf_free(char *buf) {
+
+  free(buf);
+
+  return 0;
+}
+
+int read_to_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen, int *preached_eof) {
+
+  /* grab from s, put onto buf, return how many bytes read */
+
+  int read_result;
+  char *buf;
+  size_t buflen;
+  size_t buf_datalen;
+
+  if (!pbuf || !pbuflen || !pbuf_datalen || !preached_eof) /* invalid parameters */
+    return -1 ;
+
+  if(s<0) {
+    log(LOG_DEBUG,"read_to_buf() received negative socket %d.",s);
+    return -1;
+  }
+
+  /* this is the point where you would grow the buffer, if you want to */
+  buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+  if (!buf) /* invalid parameter */
+    return -1;
+
+  read_result = read(s, buf+buf_datalen, buflen - buf_datalen);
+  if (read_result < 0) {
+    if(errno!=EAGAIN) { /* it's a real error */
+      return -1;
+    }
+    return 0;
+  } else if (read_result == 0) {
+    log(LOG_DEBUG,"read_to_buf(): Encountered eof");
+    *preached_eof = 1;
+    return 0;
+  } else { /* we read some bytes */
+    *pbuf_datalen = buf_datalen + read_result;
+    log(LOG_DEBUG,"read_to_buf(): Read %d bytes. %d on inbuf.",read_result, *pbuf_datalen);
+    return read_result;
+  }
+
+}
+
+int flush_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+  /* push from buf onto s
+   * then memmove to front of buf
+   * return -1 or how many bytes remain on the buf */
+
+  int write_result;
+  char *buf;
+  size_t buflen;
+  size_t buf_datalen;
+
+  if (!pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+    return -1;
+
+  if(s<0) {
+    log(LOG_DEBUG,"flush_buf() received negative socket %d.",s);
+    return -1;
+  }
+
+
+  if(*pbuf_datalen == 0) /* nothing to flush */
+    return 0;
+
+  /* this is the point where you would grow the buffer, if you want to */
+  buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+  if (!buf) /* invalid parameter */
+    return -1;
+
+  write_result = write(s, buf, buf_datalen);
+  if (write_result < 0) {
+    if(errno!=EAGAIN) { /* it's a real error */
+      return -1;
+    }
+    log(LOG_DEBUG,"flush_buf(): write() would block, returning.");
+    return 0;
+  } else {
+    *pbuf_datalen -= write_result;
+    memmove(buf, buf+write_result, *pbuf_datalen);
+    log(LOG_DEBUG,"flush_buf(): flushed %d bytes, %d remain.",write_result,*pbuf_datalen);
+    return *pbuf_datalen;
+  }
+
+}
+
+int write_to_buf(char *string, size_t string_len,
+                 char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+  /* append string to buf (growing as needed, return -1 if "too big")
+   * return total number of bytes on the buf
+   */
+
+  char *buf;
+  size_t buflen;
+  size_t buf_datalen;
+
+  if (!string || !pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+    return -1;
+
+  /* this is the point where you would grow the buffer, if you want to */
+  buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+  if (!buf) /* invalid parameter */
+    return -1;
+
+  if (string_len + buf_datalen > buflen) { /* we're out of luck */
+    log(LOG_DEBUG, "write_to_buf(): buflen too small. Time to implement growing dynamic bufs.");
+    return -1;
+  }
+
+  memcpy(buf+buf_datalen, string, string_len);
+  *pbuf_datalen += string_len;
+  log(LOG_DEBUG,"write_to_buf(): added %d bytes to buf (now %d total).",string_len, *pbuf_datalen);
+  return *pbuf_datalen;
+
+}
+
+int fetch_from_buf(char *string, size_t string_len,
+                 char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) {
+
+  /* if there is string_len bytes in buf, write them onto string,
+   * then memmove buf back (that is, remove them from buf) */
+
+  char *buf;
+  size_t buflen;
+  size_t buf_datalen;
+
+  if (!string || !pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */
+    return -1;
+
+  /* this is the point where you would grow the buffer, if you want to */
+  buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen;
+
+  if (!buf) /* invalid parameter */
+    return -1;
+
+  if(string_len > buf_datalen) /* we want too much. sorry. */
+    return -1;
+ 
+  memcpy(string,buf,string_len);
+  *pbuf_datalen -= string_len;
+  memmove(buf, buf+string_len, *pbuf_datalen);
+  return *pbuf_datalen;
+
+}
+

+ 23 - 0
src/or/cell.c

@@ -0,0 +1,23 @@
+
+#include "or.h"
+
+int check_sane_cell(cell_t *cell) {
+
+  if(!cell)
+    return -1;
+
+  if(cell->aci == 0) {
+    log(LOG_DEBUG,"check_sane_cell(): Cell has aci=0. Dropping.");
+    return -1;
+  }
+
+#if 0 /* actually, the length is sometimes encrypted. so it's ok. */
+  if(cell->length > 120) {
+    log(LOG_DEBUG,"check_sane_cell(): Cell claims to have payload length %d. Dropping.",cell->length);
+    return -1;
+  }
+#endif
+
+  return 0; /* looks good */
+}
+

+ 319 - 0
src/or/circuit.c

@@ -0,0 +1,319 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+circuit_t *global_circuitlist=NULL;
+
+/********* END VARIABLES ************/
+
+void circuit_add(circuit_t *circ) {
+
+  if(!global_circuitlist) { /* first one */
+    global_circuitlist = circ;
+    circ->next = NULL;
+  } else {
+    circ->next = global_circuitlist;
+    global_circuitlist = circ;
+  }
+
+}
+
+void circuit_remove(circuit_t *circ) {
+  circuit_t *tmpcirc;
+
+  if(!circ || !global_circuitlist)
+    return;
+
+  if(global_circuitlist == circ) {
+    global_circuitlist = global_circuitlist->next;
+    return;
+  }
+
+  for(tmpcirc = global_circuitlist;tmpcirc->next;tmpcirc = tmpcirc->next) {
+    if(tmpcirc->next == circ) {
+      tmpcirc->next = circ->next;
+      return;
+    }
+  }
+
+}
+
+circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn) {
+
+  circuit_t *circ; 
+
+  circ = (circuit_t *)malloc(sizeof(circuit_t));
+  if(!circ)
+    return NULL;
+  memset(circ,0,sizeof(circuit_t)); /* zero it out */
+
+  circ->p_aci = p_aci;
+  circ->p_conn = p_conn;
+
+  circ->state = CIRCUIT_STATE_OPEN_WAIT;
+  circ->onion = NULL;
+  circ->onionlen=0;
+  circ->recvlen=0;
+
+  /* ACIs */
+  circ->p_aci = p_aci;
+  circ->n_aci = 0; /* we need to have identified the next hop to choose a correct ACI */
+
+  circuit_add(circ);
+
+  return circ;
+}
+
+void circuit_free(circuit_t *circ) {
+
+  EVP_CIPHER_CTX_cleanup(&circ->n_ctx);
+  EVP_CIPHER_CTX_cleanup(&circ->p_ctx);
+
+  if(circ->onion)
+    free(circ->onion);
+
+  free(circ);
+
+}
+
+aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) {
+  aci_t test_aci;
+  connection_t *conn;
+
+  log(LOG_DEBUG,"get_unique_aci_by_addr_port() trying to get a unique aci");
+
+  RAND_pseudo_bytes((unsigned char *)&test_aci, 2);
+
+  if(aci_type == ACI_TYPE_LOWER)
+    test_aci &= htons(0x00FF);
+  if(aci_type == ACI_TYPE_HIGHER)
+    test_aci &= htons(0xFF00);
+
+  if(test_aci == 0)
+    return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */ 
+
+  conn = connection_get_by_addr_port(addr,port);  
+  if(!conn) /* there can't be a conflict -- no connection of that sort yet */
+    return test_aci;
+
+  if(circuit_get_by_aci_conn(test_aci, conn))
+    return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */
+
+  return test_aci;
+  
+}
+
+int circuit_init(circuit_t *circ, int aci_type) {
+  onion_layer_t *ol;
+  int retval = 0;
+  unsigned char digest1[20];
+  unsigned char digest2[20];
+
+  if (!circ)
+    return -1;
+
+  ol = (onion_layer_t *)circ->onion;
+  if (!ol)
+    return -1;
+
+  log(LOG_DEBUG,"circuit_init(): starting");
+  circ->n_addr = ol->addr;
+  circ->n_port = ol->port;
+  log(LOG_DEBUG,"circuit_init(): Set port to %u.",ntohs(ol->port));
+  circ->p_f = ol->backf;
+  log(LOG_DEBUG,"circuit_init(): Set BACKF to %u.",ol->backf);
+  circ->n_f = ol->forwf;
+  log(LOG_DEBUG,"circuit_init(): Set FORWF to %u.",ol->forwf);
+  circ->state = CIRCUIT_STATE_OPEN;
+
+  log(LOG_DEBUG,"circuit_init(): aci_type = %u.",aci_type);
+
+  circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type);
+
+  log(LOG_DEBUG,"circuit_init(): Chosen ACI %u.",circ->n_aci);
+
+  /* keys */
+  SHA1(ol->keyseed,16,digest1);
+  SHA1(digest1,20,digest2);
+  SHA1(digest2,20,digest1);
+  memcpy(circ->p_key,digest2,16);
+  memcpy(circ->n_key,digest1,16);
+  log(LOG_DEBUG,"circuit_init(): Computed keys.");
+
+  /* set IVs to zero */
+  memset(circ->n_iv,0,16);
+  memset(circ->p_iv,0,16);
+
+  /* initialize cipher context */
+  EVP_CIPHER_CTX_init(&circ->n_ctx);
+  EVP_CIPHER_CTX_init(&circ->p_ctx);
+
+  /* initialize crypto engines */
+  switch(circ->p_f)
+  {
+   case ONION_CIPHER_DES :
+    retval = EVP_EncryptInit(&circ->p_ctx, EVP_des_ofb(), circ->p_key, circ->p_iv);
+    break;
+   case ONION_CIPHER_RC4 :
+    retval = EVP_EncryptInit(&circ->p_ctx, EVP_rc4(), circ->p_key,circ->p_iv);
+    break;
+   case ONION_CIPHER_IDENTITY :
+    retval = EVP_EncryptInit(&circ->p_ctx, EVP_enc_null(), circ->p_key, circ->p_iv);
+    break;
+   default :
+    log(LOG_ERR,"Onion contains unrecognized cipher(%u) for ACI : %u.",circ->p_f,circ->n_aci);
+    return -1;
+    break;
+  }
+
+  if (!retval) /* EVP_EncryptInit() error */
+  {
+    log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci);
+    EVP_CIPHER_CTX_cleanup(&circ->n_ctx);
+    EVP_CIPHER_CTX_cleanup(&circ->p_ctx);
+    return -1;
+  }
+  switch(circ->n_f)
+  {
+   case ONION_CIPHER_DES :
+    retval = EVP_DecryptInit(&circ->n_ctx, EVP_des_ofb(), circ->n_key, circ->n_iv);
+    break;
+   case ONION_CIPHER_RC4 :
+    retval = EVP_DecryptInit(&circ->n_ctx, EVP_rc4(), circ->n_key,circ->n_iv);
+    break;
+   case ONION_CIPHER_IDENTITY :
+    retval = EVP_DecryptInit(&circ->n_ctx, EVP_enc_null(), circ->n_key, circ->n_iv);
+    break;
+   default :
+    log(LOG_ERR,"Onion contains unrecognized cipher for ACI : %u.",circ->n_aci);
+    return -1;
+    break;
+  }
+  if (!retval) /* EVP_EncryptInit() error */
+  {
+    log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci);
+    EVP_CIPHER_CTX_cleanup(&circ->n_ctx);
+    EVP_CIPHER_CTX_cleanup(&circ->p_ctx);
+    return -1;
+  }
+  log(LOG_DEBUG,"circuit_init(): Cipher initialization complete.");
+
+  circ->expire = ol->expire;
+
+  return 0;
+}
+
+circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn) {
+  circuit_t *circ;
+
+  for(circ=global_circuitlist;circ;circ = circ->next) {
+    if(circ->p_conn == conn && circ->p_aci == aci)
+       return circ;
+    if(circ->n_conn == conn && circ->n_aci == aci)
+       return circ;
+  }
+  return NULL;
+}
+
+circuit_t *circuit_get_by_conn(connection_t *conn) {
+  circuit_t *circ;
+
+  for(circ=global_circuitlist;circ;circ = circ->next) {
+    if(circ->p_conn == conn)
+       return circ;
+    if(circ->n_conn == conn)
+       return circ;
+  }
+  return NULL;
+}
+
+int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type) {
+
+  /* first decrypt cell->length */
+  if(circuit_crypt(circ, &(cell->length), 1, crypt_type) < 0) {
+    log(LOG_DEBUG,"circuit_deliver_data_cell(): length decryption failed. Dropping connection.");
+    return -1;
+  }
+
+  /* then decrypt the payload */
+  if(circuit_crypt(circ, (char *)&(cell->payload), CELL_PAYLOAD_SIZE, crypt_type) < 0) {
+    log(LOG_DEBUG,"circuit_deliver_data_cell(): payload decryption failed. Dropping connection.");
+    return -1;
+  }
+
+  if(conn->type == CONN_TYPE_APP) { /* send payload directly */
+    log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to application.");
+    if(connection_app_process_data_cell(cell, conn) < 0) {
+      return -1;
+    }
+  } else { /* send it as a cell */
+    log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to connection.");
+    if(connection_write_cell_to_buf(cell, conn) < 0) {
+      return -1;
+    }
+  }
+  return 0; /* success */
+}
+
+int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) {
+  char *out;
+  int outlen;
+
+  if(!circ || !in)
+    return -1;
+
+  out = malloc(inlen);
+  if(!out)
+    return -1;
+
+  if(crypt_type == 'e') {
+    log(LOG_DEBUG,"circuit_crypt(): Encrypting %d bytes.",inlen);
+    if(!EVP_EncryptUpdate(&circ->p_ctx,out,&outlen,in,inlen)) {
+      log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).",circ->p_aci, ERR_reason_error_string(ERR_get_error()));
+      return -1;
+    }
+  } else if(crypt_type == 'd') {
+    log(LOG_DEBUG,"circuit_crypt(): Decrypting %d bytes.",inlen);
+    if(!EVP_DecryptUpdate(&circ->n_ctx,out,&outlen,in,inlen)) {
+      log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).",circ->n_aci, ERR_reason_error_string(ERR_get_error()));
+      return -1;
+    }
+  }
+
+  if(outlen != inlen) {
+    log(LOG_DEBUG,"circuit_crypt(): %d bytes crypted to %d bytes. Weird.",inlen,outlen);
+    return -1;
+  }
+  
+  memcpy(in,out,inlen);
+  free(out);
+
+  return 0;
+}
+
+void circuit_close(circuit_t *circ) {
+  circuit_remove(circ);
+  connection_send_destroy(circ->n_aci, circ->n_conn); 
+  connection_send_destroy(circ->p_aci, circ->p_conn); 
+  circuit_free(circ);
+}
+
+void circuit_about_to_close_connection(connection_t *conn) {
+  /* send destroys for all circuits using conn */
+  /* currently, we assume it's too late to flush conn's buf here.
+   * down the road, maybe we'll consider that eof doesn't mean can't-write
+   */
+
+  circuit_t *circ;
+
+  while((circ = circuit_get_by_conn(conn))) {
+    circuit_remove(circ);
+    if(circ->n_conn == conn) /* it's closing in front of us */
+      connection_send_destroy(circ->p_aci, circ->p_conn);
+    if(circ->p_conn == conn) /* it's closing behind us */
+      connection_send_destroy(circ->n_aci, circ->n_conn);
+    circuit_free(circ);
+  }  
+}
+

+ 212 - 0
src/or/command.c

@@ -0,0 +1,212 @@
+
+#include "or.h"
+
+void command_process_cell(cell_t *cell, connection_t *conn) {
+
+  if(check_sane_cell(cell) < 0)
+    return;
+
+  switch(cell->command) {
+    case CELL_PADDING:
+      /* do nothing */
+      break;
+    case CELL_CREATE:
+      command_process_create_cell(cell, conn);
+      break;
+    case CELL_DATA:
+      command_process_data_cell(cell, conn);
+      break;
+    case CELL_DESTROY:
+      command_process_destroy_cell(cell, conn);
+      break;
+  }
+}
+
+void command_process_create_cell(cell_t *cell, connection_t *conn) {
+  circuit_t *circ;
+  connection_t *n_conn;
+  unsigned char *cellbuf; /* array of cells */
+  int cellbuflen; /* size of cellbuf in bytes */
+  cell_t *tmpcell; /* pointer to an arbitrary cell */
+  int retval, i;
+
+  circ = circuit_get_by_aci_conn(cell->aci, conn);
+
+  if(circ && circ->state != CIRCUIT_STATE_OPEN_WAIT) {
+    log(LOG_DEBUG,"command_process_create_cell(): received CREATE cell, not in open_wait. Dropping.");
+    return;
+  }
+
+  if(!circ) { /* if it's not there, create it */
+    circ = circuit_new(cell->aci, conn);
+    circ->state = CIRCUIT_STATE_OPEN_WAIT;
+    memcpy((void *)&circ->onionlen,(void *)cell->payload, 4);
+    circ->onionlen = ntohl(circ->onionlen);
+    log(LOG_DEBUG,"command_process_create_cell():  Onion length is %u.",circ->onionlen);
+    if(circ->onionlen > 50000) { /* too big */
+      log(LOG_DEBUG,"That's ludicrous. Closing.");
+      circuit_close(circ);
+      return;
+    }
+    circ->onion = (unsigned char *)malloc(circ->onionlen);
+    if(!circ->onion) { 
+      log(LOG_DEBUG,"command_process_create_cell(): Out of memory. Closing.");
+      circuit_close(circ);
+      return;
+    }
+    if(circ->onionlen < cell->length-4) { /* protect from buffer overflow */
+      log(LOG_DEBUG,"command_process_create_cell(): Onion too small. Closing.");
+      circuit_close(circ);
+      return;
+    }
+    memcpy((void *)circ->onion,(void *)(cell->payload+4),cell->length-4);
+    circ->recvlen = cell->length-4;
+    log(LOG_DEBUG,"command_process_create_cell(): Primary create cell handled, have received %d of %d onion bytes.",
+        circ->recvlen,circ->onionlen);
+
+  } else { /* pull over as much of the onion as we can */
+    if(cell->length + circ->recvlen > circ->onionlen) { /* protect from buffer overflow */
+      log(LOG_DEBUG,"command_process_create_cell(): payload too big for onion. Closing.");
+      circuit_close(circ);
+      return;
+    }
+    memcpy((void *)(circ->onion+circ->recvlen),(void *)cell->payload,cell->length);
+    circ->recvlen += cell->length;
+    log(LOG_DEBUG,"command_process_create_cell(): Secondary create cell handled, have received %d of %d onion bytes.",
+        circ->recvlen,circ->onionlen);
+  }
+
+  if(circ->recvlen != circ->onionlen) {
+    log(LOG_DEBUG,"command_process_create_cell(): Onion not all here yet. Ok.");
+    return;
+  }
+
+  /* we're all ready to go now. */ 
+  circ->state = CIRCUIT_STATE_OPEN;
+
+  if(process_onion(circ, conn) < 0) {
+    log(LOG_DEBUG,"command_process_create_cell(): Onion processing failed. Closing.");
+    circuit_close(circ);
+    return;
+  }
+
+  if(circ->n_addr && circ->n_port) { /* must send create cells to the next router */
+    n_conn = connection_get_by_addr_port(circ->n_addr,circ->n_port);
+    if(!n_conn || n_conn->type != CONN_TYPE_OR) {
+      /* i've disabled making connections through OPs, but it's definitely
+       * possible here. I'm not sure if it would be a bug or a feature. -RD
+       */
+      log(LOG_DEBUG,"command_process_create_cell(): Next router not connected. Closing.");
+      circuit_close(circ);
+    }
+    circ->n_conn = n_conn;
+    log(LOG_DEBUG,"command_process_create_cell(): n_conn is %s:%u",n_conn->address,ntohs(n_conn->port));
+
+    /* send the CREATE cells on to the next hop  */
+    pad_onion(circ->onion,circ->onionlen, sizeof(onion_layer_t));
+    log(LOG_DEBUG,"command_process_create_cell(): Padded the onion with random data.");
+
+    retval = pack_create(circ->n_aci, circ->onion, circ->onionlen, &cellbuf, &cellbuflen);
+    free((void *)circ->onion);
+    circ->onion = NULL;
+    if (retval == -1) /* pack_create() error */
+    {
+      log(LOG_DEBUG,"command_process_create_cell(): Could not pack the onion into CREATE cells. Closing the connection.");
+      circuit_close(circ);
+      return;
+    }
+    log(LOG_DEBUG,"command_process_create_cell(): Onion packed into CREATE cells. Buffering the cells.");
+    /* queue the create cells for transmission to the next hop */
+    tmpcell = (cell_t *)cellbuf;
+    for (i=0;i<cellbuflen/sizeof(cell_t);i++)
+    {
+      retval = connection_write_cell_to_buf(tmpcell, n_conn);
+      if (retval == -1) /* buffering failed, drop the connection */
+      {
+        log(LOG_DEBUG,"command_process_create_cell(): Could not buffer new create cells. Closing.");
+        circuit_close(circ);
+        return;
+      }
+      tmpcell++;
+    }
+    free((void *)cellbuf);
+    return;
+
+  } else { /* this is destined for an app */
+    log(LOG_DEBUG,"command_process_create_cell(): Creating new application connection.");
+    n_conn = connection_new(CONN_TYPE_APP);
+    if(!n_conn) {
+      log(LOG_DEBUG,"command_process_create_cell(): connection_new failed. Closing.");
+      circuit_close(circ);
+      return;
+    }
+    n_conn->state = APP_CONN_STATE_CONNECTING_WAIT;
+    n_conn->s = -1; /* not yet valid */
+    if(connection_add(n_conn) < 0) { /* no space, forget it */
+      log(LOG_DEBUG,"command_process_create_cell(): connection_add failed. Closing.");
+      connection_free(n_conn);
+      circuit_close(circ);
+      return;
+    }
+    circ->n_conn = n_conn;
+    return;
+  }
+
+}
+
+void command_process_data_cell(cell_t *cell, connection_t *conn) {
+  circuit_t *circ;
+
+  /* FIXME do something with 'close' state, here */
+
+  circ = circuit_get_by_aci_conn(cell->aci, conn);
+
+  if(!circ) {
+    log(LOG_DEBUG,"command_process_data_cell(): received DATA cell for unknown circuit. Dropping.");
+    return;
+  }
+
+  if(circ->state == CIRCUIT_STATE_OPEN_WAIT) {
+    log(LOG_DEBUG,"command_process_data_cell(): circuit in open_wait. Dropping data cell.");
+    return;
+  }
+
+  /* at this point both circ->n_conn and circ->p_conn are guaranteed to be set */
+
+  if(cell->aci == circ->p_aci) { /* it's an outgoing cell */
+    cell->aci = circ->n_aci; /* switch it */
+    if(circuit_deliver_data_cell(cell, circ, circ->n_conn, 'd') < 0) {
+      log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (forward) failed. Closing.");
+      circuit_close(circ);
+      return;
+    }
+  } else { /* it's an ingoing cell */
+    cell->aci = circ->p_aci; /* switch it */
+    if(circuit_deliver_data_cell(cell, circ, circ->p_conn, 'e') < 0) {
+      log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (backward) failed. Closing.");
+      circuit_close(circ);
+      return;
+    }
+  }
+}
+
+void command_process_destroy_cell(cell_t *cell, connection_t *conn) {
+  circuit_t *circ;
+
+  circ = circuit_get_by_aci_conn(cell->aci, conn);
+
+  if(!circ) {
+    log(LOG_DEBUG,"command_process_destroy_cell(): received DESTROY cell for unknown circuit. Dropping.");
+    return;
+  }
+
+  log(LOG_DEBUG,"command_process_destroy_cell(): Received for aci %d.",cell->aci);
+  circuit_remove(circ);
+  if(cell->aci == circ->p_aci) /* the destroy came from behind */
+    connection_send_destroy(circ->n_aci, circ->n_conn);
+  if(cell->aci == circ->n_aci) /* the destroy came from ahead */
+    connection_send_destroy(circ->p_aci, circ->p_conn);
+  circuit_free(circ);
+
+}
+

+ 49 - 0
src/or/config.c

@@ -0,0 +1,49 @@
+/**
+ * config.c 
+ * Routines for loading the configuration file.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.3  2002/04/02 14:28:24  badbytes
+ * Final finishes.
+ *
+ * Revision 1.2  2002/01/27 00:42:50  mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.1  2002/01/03 10:24:05  badbytes
+ * COde based on that in op. Needs to be modified.
+ *
+ */
+
+#include "or.h"
+
+/* loads the configuration file */
+int getconfig(char *conf_filename, config_opt_t *options)
+{
+  FILE *cf = NULL;
+  int retval = 0;
+  
+  if ((!conf_filename) || (!options))
+    return -1;
+  
+  /* load config file */
+  cf = open_config(conf_filename);
+  if (!cf)
+  {
+    log(LOG_ERR,"Could not open configuration file %s.",conf_filename);
+    return -1;
+  }
+  retval = parse_config(cf,options);
+  if (retval)
+    return -1;
+
+  return 0;
+}
+

+ 362 - 0
src/or/connection.c

@@ -0,0 +1,362 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+char *conn_type_to_string[] = {
+  "OP listener", /* 0 */
+  "OP",          /* 1 */
+  "OR listener", /* 2 */
+  "OR",          /* 3 */
+  "App"          /* 4 */
+};
+
+char *conn_state_to_string[][10] = {
+  { "ready" }, /* op listener, 0 */
+  { "awaiting keys", /* op, 0 */
+    "open",              /* 1 */
+    "close",             /* 2 */
+    "close_wait" },      /* 3 */
+  { "ready" }, /* or listener, 0 */
+  { "connecting (as client)",   /* or, 0 */
+    "sending auth (as client)",     /* 1 */
+    "waiting for auth (as client)", /* 2 */
+    "sending nonce (as client)",    /* 3 */
+    "waiting for auth (as server)", /* 4 */
+    "sending auth (as server)",     /* 5 */
+    "waiting for nonce (as server)",/* 6 */
+    "open" },                       /* 7 */
+  { "connecting",                 /* app, 0 */
+    "open",                            /* 1 */
+    "waiting for dest info",           /* 2 */
+    "flushing buffer, then will close",/* 3 */
+    "close_wait" }                     /* 4 */
+};
+
+/********* END VARIABLES ************/
+
+connection_t *connection_new(int type) {
+  connection_t *conn;
+
+  conn = (connection_t *)malloc(sizeof(connection_t));
+  if(!conn)
+    return NULL;
+  memset(conn,0,sizeof(connection_t)); /* zero it out to start */
+
+  conn->type = type;
+  buf_new(&conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen);
+  buf_new(&conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+
+  return conn;
+}
+
+void connection_free(connection_t *conn) {
+  assert(conn);
+
+  buf_free(conn->inbuf);
+  buf_free(conn->outbuf);
+  if(conn->address)
+    free(conn->address);
+
+ /* FIXME should we do these for all connections, or just ORs, or what */
+  if(conn->type == CONN_TYPE_OR ||
+     conn->type == CONN_TYPE_OP) {
+//    EVP_CIPHER_CTX_cleanup(&conn->f_ctx);
+//    EVP_CIPHER_CTX_cleanup(&conn->b_ctx);
+  }
+
+  if(conn->s > 0)
+    close(conn->s);
+  free(conn);
+}
+
+int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type) {
+  connection_t *conn;
+  int s;
+  int one=1;
+
+  s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (s < 0)
+  { 
+    log(LOG_ERR,"connection_create_listener(): Socket creation failed.");
+    return -1;
+  }
+
+  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+  if(bind(s,(struct sockaddr *)local,sizeof(*local)) < 0) {
+    perror("bind ");
+    log(LOG_ERR,"Could not bind to local port %u.",ntohs(local->sin_port));
+    return -1;
+  }
+
+  /* start local server */
+  if(listen(s,SOMAXCONN) < 0) {
+    log(LOG_ERR,"Could not listen on local port %u.",ntohs(local->sin_port));
+    return -1;
+  }
+
+  fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
+
+  conn = connection_new(type);
+  conn->s = s;
+
+  if(connection_add(conn) < 0) { /* no space, forget it */
+    connection_free(conn);
+    return -1;
+  }
+
+  /* remember things so you can tell the baby sockets */
+  memcpy(&conn->local,local,sizeof(struct sockaddr_in));
+  conn->prkey = prkey;
+
+  log(LOG_DEBUG,"connection_create_listener(): Listening on local port %u.",ntohs(local->sin_port));
+
+  conn->state = LISTENER_STATE_READY;
+  connection_watch_events(conn, POLLIN);
+
+  return 0;
+}
+
+int connection_handle_listener_read(connection_t *conn, int new_type, int new_state) {
+
+  int news; /* the new socket */
+  connection_t *newconn;
+  struct sockaddr_in remote; /* information about the remote peer when connecting to other routers */
+  int remotelen = sizeof(struct sockaddr_in); /* length of the remote address */
+
+  news = accept(conn->s,(struct sockaddr *)&remote,&remotelen);
+  if (news == -1) { /* accept() error */
+    if(errno==EAGAIN)
+      return 0; /* he hung up before we could accept(). that's fine. */
+    /* else there was a real error. */
+    log(LOG_ERR,"connection_handle_listener_read(): accept() failed. Closing.");
+    return -1;
+  }
+  log(LOG_DEBUG,"Connection accepted on socket %d.",news);
+
+  newconn = connection_new(new_type);
+  newconn->s = news;
+
+  /* learn things from parent, so we can perform auth */
+  memcpy(&newconn->local,&conn->local,sizeof(struct sockaddr_in));
+  newconn->prkey = conn->prkey;
+//  newconn->address = strdup(get_string_from_remote()) FIXME ;
+
+  if(connection_add(newconn) < 0) { /* no space, forget it */
+    connection_free(newconn);
+    return -1;
+  }
+
+  log(LOG_DEBUG,"connection_handle_listener_read(): socket %d entered state %d.",newconn->s, new_state);
+  newconn->state = new_state;
+  connection_watch_events(newconn, POLLIN);
+
+  return 0;
+}
+
+int retry_all_connections(routerinfo_t **router_array, int rarray_len,
+  RSA *prkey, uint16_t or_port, uint16_t op_port) {
+
+  /* start all connections that should be up but aren't */
+
+  routerinfo_t *router;
+  int i;
+
+  /* local host information */
+  char localhostname[512];
+  struct hostent *localhost;
+  struct sockaddr_in local; /* local address */
+
+  /* obtain local host information */
+  if(gethostname(localhostname,512) < 0) {
+    log(LOG_ERR,"Error obtaining local hostname.");
+    return -1;
+  }
+  localhost = gethostbyname(localhostname);
+  if (!localhost) {
+    log(LOG_ERR,"Error obtaining local host info.");
+    return -1;
+  }
+  memset((void *)&local,0,sizeof(local));
+  local.sin_family = AF_INET;
+  local.sin_addr.s_addr = INADDR_ANY;
+  local.sin_port = htons(or_port);
+  memcpy((void *)&local.sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr));
+
+  for (i=0;i<rarray_len;i++) {
+    router = router_array[i];
+    if(!connection_get_by_addr_port(router->addr,router->port)) { /* not in the list */
+      connect_to_router(router, prkey, &local);
+    }
+  }
+
+  if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) {
+    connection_or_create_listener(prkey, &local);
+  }
+      
+  local.sin_port = htons(op_port);
+  if(!connection_get_by_type(CONN_TYPE_OP_LISTENER)) {
+    connection_op_create_listener(prkey, &local);
+  }
+ 
+  return 0;
+
+}
+
+int connection_read_to_buf(connection_t *conn) {
+  return read_to_buf(conn->s, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen, &conn->inbuf_reached_eof);
+}
+
+int connection_fetch_from_buf(char *string, int len, connection_t *conn) {
+  return fetch_from_buf(string, len, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen);
+}
+
+int connection_flush_buf(connection_t *conn) {
+  return flush_buf(conn->s, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+}
+
+int connection_write_to_buf(char *string, int len, connection_t *conn) {
+  if(!len)
+    return 0;
+  connection_watch_events(conn, POLLOUT | POLLIN);
+  return write_to_buf(string, len, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+}
+
+int connection_send_destroy(aci_t aci, connection_t *conn) {
+  cell_t cell;
+
+  if(!conn)
+    return -1;
+
+  if(conn->type == CONN_TYPE_OP ||
+     conn->type == CONN_TYPE_APP) {
+     log(LOG_DEBUG,"connection_send_destroy(): At an edge. Marking connection for close.");
+     conn->marked_for_close = 1;
+     return 0;
+  }
+
+  cell.aci = aci;
+  cell.command = CELL_DESTROY;
+  log(LOG_DEBUG,"connection_send_destroy(): Sending destroy (aci %d).",aci);
+  return connection_write_cell_to_buf(&cell, conn);
+
+}
+
+int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn) {
+  /* FIXME in the future, we should modify windows, etc, here */
+ 
+  if(connection_encrypt_cell_header(cellp,conn)<0) {
+    return -1;
+  }
+
+  return connection_write_to_buf((char *)cellp, sizeof(cell_t), conn);
+
+}
+
+int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn) {
+  char newheader[8];
+  int newsize;
+  int x;
+  char *px;
+
+  printf("Sending: Cell header plaintext: ");
+  px = (char *)cellp;
+  for(x=0;x<8;x++) {
+    printf("%u ",px[x]);
+  } 
+  printf("\n");
+
+  if(!EVP_EncryptUpdate(&conn->f_ctx, newheader, &newsize, (char *)cellp, 8)) {
+    log(LOG_ERR,"Could not encrypt data for connection %s:%u.",conn->address,ntohs(conn->port));
+    return -1;
+  }
+  printf("Sending: Cell header crypttext: ");
+  for(x=0;x<8;x++) {
+    printf("%u ",newheader[x]);
+  }
+  printf("\n");
+
+  memcpy(cellp,newheader,8);
+  return 0;
+}
+
+int connection_process_inbuf(connection_t *conn) {
+
+  assert(conn);
+
+  switch(conn->type) {
+    case CONN_TYPE_OP:
+      return connection_op_process_inbuf(conn);
+    case CONN_TYPE_OR:
+      return connection_or_process_inbuf(conn);
+    case CONN_TYPE_APP:
+      return connection_app_process_inbuf(conn);
+    default:
+      log(LOG_DEBUG,"connection_process_inbuf() got unexpected conn->type.");
+      return -1;
+  }
+}
+
+int connection_finished_flushing(connection_t *conn) {
+
+  assert(conn);
+
+  log(LOG_DEBUG,"connection_finished_flushing() entered. Socket %u.", conn->s);
+
+  switch(conn->type) {
+    case CONN_TYPE_OP:
+      return connection_op_finished_flushing(conn);
+    case CONN_TYPE_OR:
+      return connection_or_finished_flushing(conn);
+    case CONN_TYPE_APP:
+      return connection_app_finished_flushing(conn);
+    default:
+      log(LOG_DEBUG,"connection_finished_flushing() got unexpected conn->type.");
+      return -1;
+  }
+}
+
+int connection_process_cell_from_inbuf(connection_t *conn) {
+  /* check if there's a whole cell there.
+   * if yes, pull it off, decrypt it, and process it.
+   */
+  char crypted[128];
+  char outbuf[1024];
+  int outlen;
+  int x;
+  cell_t *cellp;
+
+  if(conn->inbuf_datalen < 128) /* entire response available? */
+    return 0; /* not yet */
+
+  if(connection_fetch_from_buf(crypted,128,conn) < 0) {
+    return -1;
+  }
+
+  printf("Cell header crypttext: ");
+  for(x=0;x<8;x++) {
+    printf("%u ",crypted[x]);
+  }
+  printf("\n");
+  /* decrypt */
+  if(!EVP_DecryptUpdate(&conn->b_ctx,(unsigned char *)outbuf,&outlen,crypted,8)) {
+    log(LOG_ERR,"connection_process_cell_from_inbuf(): Decryption failed, dropping.");
+    return connection_process_inbuf(conn); /* process the remainder of the buffer */
+  }
+  log(LOG_DEBUG,"connection_process_cell_from_inbuf(): Cell decrypted (%d bytes).",outlen);
+  printf("Cell header plaintext: ");
+  for(x=0;x<8;x++) {
+    printf("%u ",outbuf[x]);
+  }
+  printf("\n");
+
+  /* copy the rest of the cell */
+  memcpy((char *)outbuf+8, (char *)crypted+8, sizeof(cell_t)-8);
+  cellp = (cell_t *)outbuf;
+  log(LOG_DEBUG,"connection_process_cell_from_inbuf(): Decrypted cell is of type %u (ACI %u).",cellp->command,cellp->aci);
+  command_process_cell(cellp, conn);
+
+  return connection_process_inbuf(conn); /* process the remainder of the buffer */
+}
+

+ 212 - 0
src/or/connection_app.c

@@ -0,0 +1,212 @@
+
+#include "or.h"
+
+connection_t *connection_app_new(void) {
+  return connection_new(CONN_TYPE_APP);
+}
+
+int connection_app_process_inbuf(connection_t *conn) {
+
+  assert(conn && conn->type == CONN_TYPE_APP);
+
+  if(conn->inbuf_reached_eof) {
+    /* eof reached, kill it. */
+    log(LOG_DEBUG,"connection_app_process_inbuf(): conn reached eof. Closing.");
+    return -1;
+  }
+
+  log(LOG_DEBUG,"connection_app_process_inbuf(): state %d.",conn->state);
+
+  switch(conn->state) {
+    case APP_CONN_STATE_CONNECTING:
+      log(LOG_DEBUG,"connection_app_process_inbuf(): text from app server while in 'connecting' state. Leaving it on buffer.");
+      return 0;
+    case APP_CONN_STATE_OPEN:
+      return connection_app_package_inbuf(conn);
+  }
+  return 0;
+
+}
+
+int connection_app_package_inbuf(connection_t *conn) {
+  int amount_to_process;
+  cell_t cell;
+  circuit_t *circ;
+
+  assert(conn && conn->type == CONN_TYPE_APP);
+
+  amount_to_process = conn->inbuf_datalen;
+
+  if(!amount_to_process)
+    return 0;
+
+  if(amount_to_process > CELL_PAYLOAD_SIZE) {
+    cell.length = CELL_PAYLOAD_SIZE;
+  } else {
+    cell.length = amount_to_process;
+  }
+
+  if(connection_fetch_from_buf(cell.payload,cell.length,conn) < 0) {
+    return -1;
+  }
+
+  circ = circuit_get_by_conn(conn);
+  if(!circ) {
+    log(LOG_DEBUG,"connection_app_package_inbuf(): conn has no circuits!");
+    return -1;
+  }
+
+  log(LOG_DEBUG,"connection_app_package_inbuf(): Packaging %d bytes.",cell.length);
+  cell.aci = circ->p_aci;
+  cell.command = CELL_DATA;
+  if(circuit_deliver_data_cell(&cell, circ, circ->p_conn, 'e') < 0) {
+    log(LOG_DEBUG,"connection_app_package_inbuf(): circuit_deliver_data_cell (backward) failed. Closing.");
+    circuit_close(circ);
+    return 0;
+  }
+  if(amount_to_process > CELL_PAYLOAD_SIZE)
+    return(connection_app_package_inbuf(conn));
+  return 0;
+}
+
+int connection_app_finished_flushing(connection_t *conn) {
+  int e, len=sizeof(e);
+
+  assert(conn && conn->type == CONN_TYPE_APP);
+
+  switch(conn->state) {
+    case APP_CONN_STATE_CONNECTING:
+      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0)  { /* not yet */
+        if(errno != EINPROGRESS){
+          /* yuck. kill it. */
+          log(LOG_DEBUG,"connection_app_finished_flushing(): in-progress connect failed. Removing.");
+          return -1;
+        } else {
+          return 0; /* no change, see if next time is better */
+        }
+      }
+      /* the connect has finished. */
+
+      log(LOG_DEBUG,"connection_app_finished_flushing() : Connection to %s:%u established.",
+          conn->address,ntohs(conn->port));
+      
+      conn->state = APP_CONN_STATE_OPEN;
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    case APP_CONN_STATE_OPEN:
+      /* FIXME down the road, we'll clear out circuits that are pending to close */
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    default:
+      log(LOG_DEBUG,"Bug: connection_app_finished_flushing() called in unexpected state.");
+      return 0;
+  }
+
+  return 0;
+}
+
+int connection_app_process_data_cell(cell_t *cell, connection_t *conn) {
+  struct hostent *rent;
+  struct sockaddr_in dest_addr;
+  int s;
+
+  /* an outgoing data cell has arrived */
+
+  assert(conn && conn->type == CONN_TYPE_APP);
+
+  switch(conn->state) {
+    case APP_CONN_STATE_CONNECTING_WAIT:
+      log(LOG_DEBUG,"connection_app_process_cell(): state is connecting_wait. cell length %d.", cell->length);
+      if(!conn->ss_received) { /* this cell contains the ss */
+        if(cell->length != sizeof(ss_t)) {
+          log(LOG_DEBUG,"connection_app_process_cell(): Supposed to contain SS but wrong size. Closing.");
+          return -1;
+        }
+        memcpy(&conn->ss, cell->payload, cell->length);
+        if(conn->ss.addr_fmt != SS_ADDR_FMT_ASCII_HOST_PORT) { /* unrecognized address format */
+          log(LOG_DEBUG,"connection_app_process_cell(): SS has unrecognized address format. Closing.");
+          return -1;
+        }
+        conn->ss_received = 1;
+	log(LOG_DEBUG,"connection_app_process_cell(): SS received.");
+      } else if (!conn->addr) { /* this cell contains the dest addr */
+        if(!memchr(cell->payload,0,cell->length)) {
+          log(LOG_DEBUG,"connection_app_process_cell(): dest_addr cell has no \\0. Closing.");
+          return -1;
+        }
+        conn->address = strdup(cell->payload);
+        rent = gethostbyname(cell->payload);
+        if (!rent) { 
+          log(LOG_ERR,"connection_app_process_cell(): Could not resolve dest addr %s.",cell->payload);
+          return -1;
+        }
+        memcpy(&conn->addr, rent->h_addr,rent->h_length); 
+	log(LOG_DEBUG,"connection_app_process_cell(): addr %s resolves to %d.",cell->payload,conn->addr);
+      } else if (!conn->port) { /* this cell contains the dest port */
+        if(!memchr(cell->payload,'\0',cell->length)) {
+          log(LOG_DEBUG,"connection_app_process_cell(): dest_port cell has no \\0. Closing.");
+          return -1;
+        }
+        conn->port = atoi(cell->payload);
+        if(!conn->port) { /* bad port */
+          log(LOG_DEBUG,"connection_app_process_cell(): dest_port cell isn't a valid number. Closing.");
+          return -1;
+        }
+        /* all the necessary info is here. Start the connect() */
+        s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+        if (s < 0)
+        {
+          log(LOG_ERR,"connection_app_process_cell(): Error creating network socket.");
+          return -1;
+        }
+        fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
+      
+        memset((void *)&dest_addr,0,sizeof(dest_addr));
+        dest_addr.sin_family = AF_INET;
+        dest_addr.sin_port = conn->port;
+        memcpy((void *)&dest_addr.sin_addr, &conn->addr, sizeof(uint32_t));
+      
+        log(LOG_DEBUG,"connection_app_process_cell(): Connecting to %s:%u.",conn->address,ntohs(conn->port)); 
+
+        if(connect(s,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0){
+          if(errno != EINPROGRESS){
+            /* yuck. kill it. */
+            log(LOG_DEBUG,"connection_app_process_cell(): Connect failed.");
+            return -1;
+          } else {
+            /* it's in progress. set state appropriately and return. */
+            conn->s = s;
+            connection_set_poll_socket(conn);
+            conn->state = APP_CONN_STATE_CONNECTING;
+      
+            /* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */
+            log(LOG_DEBUG,"connection_app_process_cell(): connect in progress, socket %d.",s);
+            connection_watch_events(conn, POLLOUT | POLLIN);
+            return 0;
+          }
+        }
+
+        /* it succeeded. we're connected. */
+        log(LOG_DEBUG,"connection_app_process_cell(): Connection to %s:%u established.",conn->address,ntohs(conn->port));
+
+        conn->s = s;
+        connection_set_poll_socket(conn);
+        conn->state = APP_CONN_STATE_OPEN;
+        connection_watch_events(conn, POLLIN);
+      } else { /* i'm not sure what this would be */
+        log(LOG_DEBUG,"connection_app_process_cell(): in connecting_wait, not sure why.");
+      }
+      return 0;
+    case APP_CONN_STATE_CONNECTING:
+      log(LOG_DEBUG,"connection_app_process_cell(): Data receiving while connecting. Queueing.");
+      /* FIXME kludge. shouldn't call write_to_buf directly. */
+      return write_to_buf(cell->payload, cell->length, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen);
+    case APP_CONN_STATE_OPEN:
+      return connection_write_to_buf(cell->payload, cell->length, conn);
+  }
+
+  return 0;
+}
+
+
+

+ 130 - 0
src/or/connection_op.c

@@ -0,0 +1,130 @@
+
+#include "or.h"
+
+connection_t *connection_op_new(void) {
+  return connection_new(CONN_TYPE_OP);
+}
+
+connection_t *connection_op_listener_new(void) {
+  return connection_new(CONN_TYPE_OP_LISTENER);
+}
+
+int connection_op_process_inbuf(connection_t *conn) {
+
+  assert(conn && conn->type == CONN_TYPE_OP);
+
+  if(conn->inbuf_reached_eof) {
+    /* eof reached, kill it. */
+    log(LOG_DEBUG,"connection_op_process_inbuf(): conn reached eof. Closing.");
+    return -1;
+  }
+
+  log(LOG_DEBUG,"connection_op_process_inbuf(): state %d.",conn->state);
+
+  switch(conn->state) {
+    case OP_CONN_STATE_AWAITING_KEYS:
+      return op_handshake_process_keys(conn);
+    case OP_CONN_STATE_OPEN:
+      return connection_process_cell_from_inbuf(conn);
+    default:
+      log(LOG_DEBUG,"connection_op_process_inbuf() called in state where I'm writing. Ignoring buf for now.")
+;
+  }
+
+  return 0;
+
+}
+
+int op_handshake_process_keys(connection_t *conn) {
+  int retval;
+  int x;
+
+  /* key exchange message */
+  unsigned char auth_cipher[128];
+  unsigned char auth_plain[128];
+
+  assert(conn);
+
+  log(LOG_DEBUG,"op_handshake_process_keys() entered.");
+
+  if(conn->inbuf_datalen < 128) /* entire response available? */
+    return 0; /* not yet */
+
+  if(connection_fetch_from_buf(auth_cipher,128,conn) < 0) {
+    return -1;
+  }
+  log(LOG_DEBUG,"op_handshake_process_keys() : Received auth.");
+
+  /* decrypt response */
+  retval = RSA_private_decrypt(128,auth_cipher,auth_plain,conn->prkey,RSA_PKCS1_PADDING);
+  if (retval == -1)
+  { 
+    log(LOG_ERR,"Decrypting keys from new OP failed.");
+    log(LOG_DEBUG,"op_handshake_process_keys() : Reason : %s.",
+        ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+
+  log(LOG_DEBUG,"Successfully decrypted keys from new OP.");
+
+  conn->bandwidth = ntohl(*((uint32_t *)auth_plain));
+
+  memcpy(conn->b_session_key, auth_plain+4, 8);
+  memcpy(conn->f_session_key, auth_plain+12, 8);
+  printf("f_session_key: ");
+  for(x=0;x<8;x++) {
+    printf("%d ",conn->f_session_key[x]);
+  }
+  printf("\nb_session_key: ");
+  for(x=0;x<8;x++) {
+    printf("%d ",conn->b_session_key[x]);
+  }
+  printf("\n");
+
+  memset((void *)conn->f_session_iv, 0, 8);
+  memset((void *)conn->b_session_iv, 0, 8);
+
+  EVP_CIPHER_CTX_init(&conn->f_ctx);
+  EVP_CIPHER_CTX_init(&conn->b_ctx);
+  EVP_EncryptInit(&conn->b_ctx, EVP_des_ofb(), conn->b_session_key, conn->b_session_iv);
+  EVP_DecryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv);
+
+#if 0
+  /* FIXME must choose conn->aci here? What does it mean for a connection to have an aci? */
+  log(LOG_DEBUG,"new_entry_connection : Chosen ACI %u.",conn->aci);
+#endif
+
+  conn->state = OP_CONN_STATE_OPEN;
+  connection_watch_events(conn, POLLIN);
+
+  return 0;
+}
+
+int connection_op_finished_flushing(connection_t *conn) {
+
+  assert(conn && conn->type == CONN_TYPE_OP);
+
+  switch(conn->state) {
+    case OP_CONN_STATE_OPEN:
+      /* FIXME down the road, we'll clear out circuits that are pending to close */
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    default:
+      log(LOG_DEBUG,"Bug: connection_op_finished_flushing() called in unexpected state.");
+      return 0;
+  }
+
+  return 0;
+
+}
+
+int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local) {
+  log(LOG_DEBUG,"connection_create_op_listener starting");
+  return connection_create_listener(prkey, local, CONN_TYPE_OP_LISTENER);
+}
+
+int connection_op_handle_listener_read(connection_t *conn) {
+  log(LOG_NOTICE,"OP: Received a connection request. Waiting for keys.");
+  return connection_handle_listener_read(conn, CONN_TYPE_OP, OP_CONN_STATE_AWAITING_KEYS);
+} 
+

+ 580 - 0
src/or/connection_or.c

@@ -0,0 +1,580 @@
+
+#include "or.h"
+
+/* 
+ *
+ * these two functions are the main ways 'in' to connection_or
+ *
+ */
+
+int connection_or_process_inbuf(connection_t *conn) {
+
+  assert(conn && conn->type == CONN_TYPE_OR);
+
+  if(conn->inbuf_reached_eof) {
+    /* eof reached, kill it. */
+    log(LOG_DEBUG,"connection_or_process_inbuf(): conn reached eof. Closing.");
+    return -1;
+  }
+
+  log(LOG_DEBUG,"connection_or_process_inbuf(): state %d.",conn->state);
+
+  switch(conn->state) {
+    case OR_CONN_STATE_CLIENT_AUTH_WAIT:
+      return or_handshake_client_process_auth(conn);
+    case OR_CONN_STATE_SERVER_AUTH_WAIT:
+      return or_handshake_server_process_auth(conn);
+    case OR_CONN_STATE_SERVER_NONCE_WAIT:
+      return or_handshake_server_process_nonce(conn);
+    case OR_CONN_STATE_OPEN:
+      return connection_process_cell_from_inbuf(conn);
+    default:
+      log(LOG_DEBUG,"connection_or_process_inbuf() called in state where I'm writing. Ignoring buf for now.");
+  }
+
+  return 0;
+
+}
+
+int connection_or_finished_flushing(connection_t *conn) {
+  int e, len=sizeof(e);
+
+  assert(conn && conn->type == CONN_TYPE_OR);
+
+  switch(conn->state) {
+    case OR_CONN_STATE_CLIENT_CONNECTING:
+      if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0)  { /* not yet */
+        if(errno != EINPROGRESS){
+          /* yuck. kill it. */
+          log(LOG_DEBUG,"connection_or_finished_flushing(): in-progress connect failed. Removing.");	
+          return -1;
+        } else {
+          return 0; /* no change, see if next time is better */
+        }
+      }
+      /* the connect has finished. */
+
+      log(LOG_DEBUG,"connection_or_finished_flushing() : Connection to router %s:%u established.",
+          conn->address,ntohs(conn->port));
+
+      return or_handshake_client_send_auth(conn);
+    case OR_CONN_STATE_CLIENT_SENDING_AUTH:
+      log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending auth.");
+      conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT;
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    case OR_CONN_STATE_CLIENT_SENDING_NONCE:
+      log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending nonce.");
+      conn_or_init_crypto(conn);
+      conn->state = OR_CONN_STATE_OPEN;
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    case OR_CONN_STATE_SERVER_SENDING_AUTH:
+      log(LOG_DEBUG,"connection_or_finished_flushing(): server finished sending auth.");
+      conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT;
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    case OR_CONN_STATE_OPEN:
+      /* FIXME down the road, we'll clear out circuits that are pending to close */
+      connection_watch_events(conn, POLLIN);
+      return 0;
+    default:
+      log(LOG_DEBUG,"Bug: connection_or_finished_flushing() called in unexpected state.");
+      return 0;
+  }
+
+  return 0;
+
+}
+
+/*********************/
+
+connection_t *connection_or_new(void) {
+  return connection_new(CONN_TYPE_OR);
+}
+
+connection_t *connection_or_new_listener(void) {
+  return connection_new(CONN_TYPE_OR_LISTENER);
+}
+
+void conn_or_init_crypto(connection_t *conn) {
+  int x;
+
+  assert(conn);
+  printf("f_session_key: ");
+  for(x=0;x<8;x++) {
+    printf("%d ",conn->f_session_key[x]);
+  }
+  printf("\nb_session_key: ");
+  for(x=0;x<8;x++) {
+    printf("%d ",conn->b_session_key[x]);
+  }
+  printf("\n");
+
+  memset(conn->f_session_iv,0,8);
+  memset(conn->b_session_iv,0,8);
+  EVP_CIPHER_CTX_init(&conn->f_ctx);
+  EVP_CIPHER_CTX_init(&conn->b_ctx);
+  EVP_EncryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv);
+  EVP_DecryptInit(&conn->b_ctx, EVP_des_ofb(), conn->b_session_key, conn->b_session_iv);
+    /* always encrypt with f, always decrypt with b */
+  
+}
+
+/*
+ *
+ * auth handshake, as performed by OR *initiating* the connection
+ *
+ */
+
+int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local) {
+  connection_t *conn;
+  struct sockaddr_in router_addr;
+  int s;
+
+  if ((!router) || (!prkey) || (!local))
+    return -1;
+
+  if(router->addr == local->sin_addr.s_addr && router->port == local->sin_port) {
+    /* this is me! don't connect to me. */
+    return 0;
+  }
+
+  conn = connection_or_new();
+
+  /* set up conn so it's got all the data we need to remember */
+  conn->addr = router->addr, conn->port = router->port;
+  conn->prkey = prkey;
+  conn->min = router->min, conn->max = router->max;
+  conn->pkey = router->pkey;
+  conn->address = strdup(router->address);
+  memcpy(&conn->local,local,sizeof(struct sockaddr_in));
+
+  s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (s < 0)
+  { 
+    log(LOG_ERR,"Error creating network socket.");
+    connection_free(conn);
+    return -1;
+  }
+  fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
+
+  memset((void *)&router_addr,0,sizeof(router_addr));
+  router_addr.sin_family = AF_INET;
+  router_addr.sin_port = router->port;
+  memcpy((void *)&router_addr.sin_addr, &router->addr, sizeof(uint32_t));
+
+  log(LOG_DEBUG,"connect_to_router() : Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),ntohs(router->port));
+
+  if(connect(s,(struct sockaddr *)&router_addr,sizeof(router_addr)) < 0){
+    if(errno != EINPROGRESS){
+      /* yuck. kill it. */
+      connection_free(conn);
+      return -1;
+    } else {
+      /* it's in progress. set state appropriately and return. */
+      conn->s = s;
+
+      conn->state = OR_CONN_STATE_CLIENT_CONNECTING;
+
+      if(connection_add(conn) < 0) { /* no space, forget it */
+        connection_free(conn);
+        return -1;
+      }
+      /* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */
+      log(LOG_DEBUG,"connect_to_router() : connect in progress.");
+      connection_watch_events(conn, POLLOUT | POLLIN);
+      return 0;
+    }
+  }
+
+  /* it succeeded. we're connected. */
+  conn->s = s;
+
+  if(connection_add(conn) < 0) { /* no space, forget it */
+    connection_free(conn);
+    return -1;
+  }
+
+  log(LOG_DEBUG,"connect_to_router() : Connection to router %s:%u established.",router->address,ntohs(router->port));
+
+  /* move to the next step in the handshake */
+  if(or_handshake_client_send_auth(conn) < 0) {
+    connection_remove(conn);
+    connection_free(conn);
+    return -1;
+  }
+  return 0; 
+}
+
+int or_handshake_client_send_auth(connection_t *conn) {
+  int retval;
+  char keys[16];
+  char buf[44];
+  char cipher[128];
+
+  if (!conn)
+    return -1;
+
+  /* generate random keys */
+  retval = RAND_bytes(keys,16);
+  if (retval != 1) /* error */
+  { 
+    log(LOG_ERR,"Cannot generate a secure DES key.");
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated DES keys.");
+
+  /* save keys */
+  memcpy(conn->f_session_key,keys,8);
+  memcpy(conn->b_session_key,keys+8,8);
+
+  /* generate first message */
+  memcpy(buf,&conn->local.sin_addr,4); /* local address */
+  memcpy(buf+4,(void *)&conn->local.sin_port,2); /* local port */
+  memcpy(buf+6, (void *)&conn->addr, 4); /* remote address */
+  memcpy(buf+10, (void *)&conn->port, 2); /* remote port */
+  memcpy(buf+12,keys,16); /* keys */
+  *((uint32_t *)(buf+28)) = htonl(conn->min); /* min link utilisation */
+  *((uint32_t *)(buf+32)) = htonl(conn->max); /* maximum link utilisation */
+  log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated first authentication message.");
+
+  /* encrypt message */
+  retval = RSA_public_encrypt(36,buf,cipher,conn->pkey,RSA_PKCS1_PADDING);
+  if (retval == -1) /* error */
+  { 
+    log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port));
+    log(LOG_DEBUG,"or_handshake_client_send_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_client_send_auth() : Encrypted authentication message.");
+
+  /* send message */
+  
+  if(connection_write_to_buf(cipher, 128, conn) < 0) {
+    log(LOG_DEBUG,"or_handshake_client_send_auth(): my outbuf is full. Oops.");
+    return -1;
+  }
+  retval = connection_flush_buf(conn);
+  if(retval < 0) {
+    log(LOG_DEBUG,"or_handshake_client_send_auth(): bad socket while flushing.");
+    return -1;
+  }
+  if(retval > 0) {
+    /* still stuff on the buffer. */
+    conn->state = OR_CONN_STATE_CLIENT_SENDING_AUTH;
+    connection_watch_events(conn, POLLOUT | POLLIN);
+    return 0;
+  }
+
+  /* it finished sending */
+  log(LOG_DEBUG,"or_handshake_client_send_auth(): Finished sending authentication message.");
+  conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT;
+  connection_watch_events(conn, POLLIN);
+  return 0;
+
+}
+
+int or_handshake_client_process_auth(connection_t *conn) {
+  char buf[128]; /* only 44 of this is expected to be used */
+  char cipher[128];
+  uint32_t min,max;
+  int retval;
+
+  assert(conn);
+
+  if(conn->inbuf_datalen < 128) /* entire response available? */
+    return 0; /* not yet */
+
+  if(connection_fetch_from_buf(cipher,128,conn) < 0) {
+    return -1;    
+  }
+  log(LOG_DEBUG,"or_handshake_client_process_auth() : Received auth.");
+
+  /* decrypt response */
+  retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
+  if (retval == -1)
+  { 
+    log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
+        conn->address,ntohs(conn->port));
+    log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",
+        ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+  else if (retval != 44)
+  { 
+    log(LOG_ERR,"Received an incorrect response from router %s:%u during authentication.",
+        conn->address,ntohs(conn->port));
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_client_process_auth() : Decrypted response.");
+  /* check validity */
+  if ( (memcmp(&conn->local.sin_addr, buf, 4)) || /* local address */
+       (memcmp(&conn->local.sin_port, buf+4, 2)) || /* local port */
+       (memcmp(&conn->addr, buf+6, 4)) || /* remote address */
+       (memcmp(&conn->port, buf+10, 2)) || /* remote port */
+       (memcmp(&conn->f_session_key, buf+12, 8)) || /* keys */
+       (memcmp(&conn->b_session_key, buf+20, 8)))
+  { /* incorrect response */
+    log(LOG_ERR,"Router %s:%u failed to authenticate. Either the key I have is obsolete or they're doing something they're not supposed to.",conn->address,ntohs(conn->port));
+    return -1;
+  }
+
+  log(LOG_DEBUG,"or_handshake_client_process_auth() : Response valid.");
+
+  /* update link info */
+  min = *(uint32_t *)(buf+28);
+  max = *(uint32_t *)(buf+32);
+  min = ntohl(min);
+  max = ntohl(max);
+
+  if (conn->min > min)
+    conn->min = min;
+  if (conn->max > max)
+    conn->max = max;
+
+  /* reply is just local addr/port, remote addr/port, nonce */
+  memcpy(buf+12, buf+36, 8);
+
+  /* encrypt reply */
+  retval = RSA_public_encrypt(20,buf,cipher,conn->pkey,RSA_PKCS1_PADDING);
+  if (retval == -1) /* error */
+  { 
+    log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port));
+    log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+
+  /* send the message */
+
+  if(connection_write_to_buf(cipher, 128, conn) < 0) {
+    log(LOG_DEBUG,"or_handshake_client_process_auth(): my outbuf is full. Oops.");
+    return -1;
+  }
+  retval = connection_flush_buf(conn);
+  if(retval < 0) {
+    log(LOG_DEBUG,"or_handshake_client_process_auth(): bad socket while flushing.");
+    return -1;
+  }
+  if(retval > 0) {
+    /* still stuff on the buffer. */
+    conn->state = OR_CONN_STATE_CLIENT_SENDING_NONCE;
+    connection_watch_events(conn, POLLOUT | POLLIN);
+/*    return(connection_process_inbuf(conn)); process the rest of the inbuf */
+    return 0;   
+  }
+
+  /* it finished sending */
+  log(LOG_DEBUG,"or_handshake_client_process_auth(): Finished sending nonce.");
+  conn_or_init_crypto(conn);
+  conn->state = OR_CONN_STATE_OPEN;
+  connection_watch_events(conn, POLLIN);
+  return connection_process_inbuf(conn); /* process the rest of the inbuf */
+
+}
+
+/*
+ * 
+ * auth handshake, as performed by OR *receiving* the connection
+ *
+ */
+
+int or_handshake_server_process_auth(connection_t *conn) {
+  int retval;
+
+  char buf[128]; /* only 42 of this is expected to be used */
+  char cipher[128];
+
+  uint32_t addr;
+  uint16_t port;
+
+  uint32_t min,max;
+  routerinfo_t *router;
+
+  assert(conn);
+
+  log(LOG_DEBUG,"or_handshake_server_process_auth() entered.");
+
+  if(conn->inbuf_datalen < 128) /* entire response available? */
+    return 0; /* not yet */  
+
+  if(connection_fetch_from_buf(cipher,128,conn) < 0) {
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_auth() : Received auth.");
+
+  /* decrypt response */
+  retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
+  if (retval == -1)
+  { 
+    log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
+        conn->address,ntohs(conn->port));
+    log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",
+        ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+  else if (retval != 36)
+  { 
+    log(LOG_ERR,"Received an incorrect authentication request.");
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_auth() : Decrypted authentication message.");
+
+  /* identify the router */
+  memcpy(&addr,buf,4); /* save the IP address */
+  memcpy(&port,buf+4,2); /* save the port */
+
+  router = router_get_by_addr_port(addr,port);
+  if (!router)
+  {
+    log(LOG_DEBUG,"or_handshake_server_process_auth() : Received a connection from an unknown router. Will drop.");
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_auth() : Router identified as %s:%u.",
+      router->address,ntohs(router->port));
+
+  if(connection_get_by_addr_port(addr,port)) {
+    log(LOG_DEBUG,"or_handshake_server_process_auth(): That router is already connected. Dropping.");
+    return -1;
+  }
+
+  /* save keys */
+  memcpy(conn->b_session_key,buf+12,8);
+  memcpy(conn->f_session_key,buf+20,8);
+
+  /* update link info */
+  min = *(uint32_t *)(buf+28);
+  max = *(uint32_t *)(buf+32);
+  min = ntohl(min);
+  max = ntohl(max);
+
+  conn->min = router->min;
+  conn->max = router->max;
+
+  if (conn->min > min)
+    conn->min = min;
+  if (conn->max > max)
+    conn->max = max;
+
+  /* copy all relevant info to conn */
+  conn->addr = router->addr, conn->port = router->port;
+  conn->pkey = router->pkey;
+  conn->address = strdup(router->address);
+
+  /* generate a nonce */
+  retval = RAND_pseudo_bytes(conn->nonce,8);
+  if (retval == -1) /* error */
+  {
+    log(LOG_ERR,"Cannot generate a nonce.");
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_auth() : Nonce generated.");
+
+  /* generate message */
+  memcpy(buf+36,conn->nonce,8); /* append the nonce to the end of the message */
+  *(uint32_t *)(buf+28) = htonl(conn->min); /* send min link utilisation */
+  *(uint32_t *)(buf+32) = htonl(conn->max); /* send max link utilisation */
+
+  /* encrypt message */
+  retval = RSA_public_encrypt(44,buf,cipher,conn->pkey,RSA_PKCS1_PADDING);
+  if (retval == -1) /* error */
+  {
+    log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port));
+    log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_auth() : Reply encrypted.");
+
+  /* send message */
+
+  if(connection_write_to_buf(cipher, 128, conn) < 0) {
+    log(LOG_DEBUG,"or_handshake_server_process_auth(): my outbuf is full. Oops.");
+    return -1;
+  }
+  retval = connection_flush_buf(conn);
+  if(retval < 0) {
+    log(LOG_DEBUG,"or_handshake_server_process_auth(): bad socket while flushing.");
+    return -1;
+  }
+  if(retval > 0) {
+    /* still stuff on the buffer. */
+    conn->state = OR_CONN_STATE_SERVER_SENDING_AUTH;
+    connection_watch_events(conn, POLLOUT | POLLIN);
+    return 0;
+  }
+
+  /* it finished sending */
+  log(LOG_DEBUG,"or_handshake_server_process_auth(): Finished sending auth.");
+  conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT;
+  connection_watch_events(conn, POLLIN);
+  return 0;
+
+}
+
+int or_handshake_server_process_nonce(connection_t *conn) {
+
+  char buf[128];
+  char cipher[128];
+  int retval;
+
+  assert(conn);
+
+  if(conn->inbuf_datalen < 128) /* entire response available? */
+    return 0; /* not yet */
+
+  if(connection_fetch_from_buf(cipher,128,conn) < 0) {
+    return -1;    
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_nonce() : Received auth.");
+
+  /* decrypt response */
+  retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
+        conn->address,ntohs(conn->port));
+    log(LOG_DEBUG,"or_handshake_server_process_nonce() : Reason : %s.",
+        ERR_reason_error_string(ERR_get_error()));
+    return -1;
+  }
+  else if (retval != 20)
+  { 
+    log(LOG_ERR,"Received an incorrect response from router %s:%u during authentication.",
+        conn->address,ntohs(conn->port));
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response decrypted.");
+
+  /* check validity */
+  if ((memcmp(&conn->addr,buf, 4)) || /* remote address */
+      (memcmp(&conn->port,buf+4,2)) || /* remote port */
+      (memcmp(&conn->local.sin_addr,buf+6,4)) || /* local address */
+      (memcmp(&conn->local.sin_port,buf+10,2)) || /* local port */
+      (memcmp(conn->nonce,buf+12,8))) /* nonce */
+  { 
+    log(LOG_ERR,"Router %s:%u failed to authenticate. Either the key I have is obsolete or they're doing something they're not supposed to.",conn->address,ntohs(conn->port));
+    return -1;
+  }
+  log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response valid. Authentication complete.");
+
+  conn_or_init_crypto(conn);
+  conn->state = OR_CONN_STATE_OPEN;
+  connection_watch_events(conn, POLLIN);
+  return connection_process_inbuf(conn); /* process the rest of the inbuf */
+
+}
+
+
+/* ********************************** */
+
+
+int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local) {
+  log(LOG_DEBUG,"connection_create_or_listener starting");
+  return connection_create_listener(prkey, local, CONN_TYPE_OR_LISTENER);
+}
+
+int connection_or_handle_listener_read(connection_t *conn) {
+  log(LOG_NOTICE,"OR: Received a connection request from a router. Attempting to authenticate.");
+  return connection_handle_listener_read(conn, CONN_TYPE_OR, OR_CONN_STATE_SERVER_AUTH_WAIT);
+}
+

+ 406 - 0
src/or/main.c

@@ -0,0 +1,406 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+/* valid command-line options */
+static char *args = "hf:e:n:l:";
+
+int loglevel = LOG_DEBUG;
+
+/* valid config file options */
+config_opt_t options[] =
+{
+  {"RouterFile", CONFIG_TYPE_STRING, {0}, 0},
+  {"PrivateKeyFile", CONFIG_TYPE_STRING, {0}, 0},
+  {"EntryPort", CONFIG_TYPE_INT, {0}, 0},
+  {"NetworkPort", CONFIG_TYPE_INT, {0}, 0},
+  {"MaxConn", CONFIG_TYPE_INT, {0}, 0},
+  {"MaxConnTimeout", CONFIG_TYPE_INT, {0}, 0},
+  {"TrafficShaping", CONFIG_TYPE_INT, {0}, 0},
+  {0}
+};
+enum opts {
+  RouterFile=0, PrivateKeyFile, EntryPort, NetworkPort, MaxConn, MaxConnTimeout, TrafficShaping
+};
+
+connection_t *connection_array[MAXCONNECTIONS] =
+        { NULL };
+
+struct pollfd poll_array[MAXCONNECTIONS] =
+        { [0 ... MAXCONNECTIONS-1] = { -1, 0, 0 } };
+
+int nfds=0; /* number of connections currently active */
+
+/* default logging threshold */
+extern int loglevel;
+
+/* private key */
+RSA *prkey = NULL;
+
+/* router array */
+routerinfo_t **router_array = NULL;
+int rarray_len = 0;
+
+/********* END VARIABLES ************/
+
+int connection_add(connection_t *conn) {
+
+  if(nfds >= MAXCONNECTIONS-2) { /* 2, for some breathing room. should count the fenceposts. */
+    /* FIXME should use the 'max connections' option */
+    log(LOG_DEBUG,"connection_add(): failing because nfds is too high.");
+    return -1;
+  }
+
+  conn->poll_index = nfds;
+  connection_set_poll_socket(conn);
+  connection_array[nfds] = conn;
+
+  /* zero these out here, because otherwise we'll inherit values from the previously freed one */
+  poll_array[nfds].events = 0;
+  poll_array[nfds].revents = 0;
+
+  nfds++;
+
+  log(LOG_DEBUG,"connection_add(): new conn type %d, socket %d, nfds %d.",conn->type, conn->s, nfds);
+
+  return 0;
+
+}
+
+void connection_set_poll_socket(connection_t *conn) {
+  poll_array[conn->poll_index].fd = conn->s;
+}
+
+int connection_remove(connection_t *conn) {
+
+  int current_index;
+
+  assert(conn);
+  assert(nfds>0);
+
+  circuit_about_to_close_connection(conn); /* flush and send destroys for all circuits on this conn */
+
+  current_index = conn->poll_index;
+  if(current_index == nfds-1) { /* this is the end */
+//    connection_free(conn);
+    nfds--;
+    log(LOG_DEBUG,"connection_remove(): nfds now %d.",nfds);  
+    return 0;
+  } 
+
+  /* we replace this one with the one at the end, then free it */
+  nfds--;
+  poll_array[current_index].fd = poll_array[nfds].fd; 
+  poll_array[current_index].events = poll_array[nfds].events;
+  poll_array[current_index].revents = poll_array[nfds].revents;
+  connection_array[current_index] = connection_array[nfds];
+  connection_array[current_index]->poll_index = current_index;
+
+  log(LOG_DEBUG,"connection_remove(): nfds now %d.",nfds);
+
+  return 0;  
+}
+
+connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port) {
+
+  int i;
+  connection_t *conn;
+
+  for(i=0;i<nfds;i++) {
+    conn = connection_array[i];
+    assert(conn);
+    if(conn->addr == addr && conn->port == port)
+       return conn;
+  }
+
+  return NULL;
+
+}
+
+connection_t *connection_get_by_type(int type) {
+
+  int i;
+  connection_t *conn;
+
+  for(i=0;i<nfds;i++) {
+    conn = connection_array[i];
+    if(conn->type == type)
+       return conn;
+  }
+
+  return NULL;
+
+}
+
+routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
+  int i;
+  routerinfo_t *router;
+
+  if (!router_array)
+    return NULL;
+
+  for(i=0;i<rarray_len;i++)
+  {
+    router = router_array[i];
+    if ((router->addr == addr) && (router->port == port))
+      return router;
+  }
+
+  return NULL;
+
+}
+
+void connection_watch_events(connection_t *conn, short events) {
+
+  assert(conn && conn->poll_index < nfds);
+
+  poll_array[conn->poll_index].events = events;
+}
+
+void check_conn_read(int i) {
+
+  int retval;
+  connection_t *conn;
+
+  if(poll_array[i].revents & POLLIN) { /* something to read */
+
+    conn = connection_array[i];
+    assert(conn);
+    log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s);
+
+    if (conn->type == CONN_TYPE_OP_LISTENER) {
+      retval = connection_op_handle_listener_read(conn);
+    } else if (conn->type == CONN_TYPE_OR_LISTENER) {
+      retval = connection_or_handle_listener_read(conn);
+    } else {
+      /* else it's an OP, OR, or app */
+      retval = connection_read_to_buf(conn);
+      if (retval >= 0) { /* all still well */
+        retval = connection_process_inbuf(conn);
+	log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval);
+      }
+    }
+  
+    if(retval < 0) { /* this connection is broken. remove it */
+      log(LOG_DEBUG,"check_conn_read(): Connection broken, removing."); 
+      connection_remove(conn);
+      connection_free(conn);
+      if(i<nfds) { /* we just replaced the one at i with a new one.
+                      process it too. */
+        check_conn_read(i);
+      }
+    }
+  }
+}
+
+void check_conn_write(int i) {
+
+  int retval;
+  connection_t *conn;
+
+  if(poll_array[i].revents & POLLOUT) { /* something to write */
+
+    conn = connection_array[i];
+    log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s);
+
+    if(conn->type == CONN_TYPE_OP_LISTENER ||
+       conn->type == CONN_TYPE_OR_LISTENER) {
+      log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!");
+      retval = -1;
+    } else {
+      /* else it's an OP, OR, or app */
+      retval = connection_flush_buf(conn); /* conns in CONNECTING state will fall through... */
+      if(retval == 0) { /* it's done flushing */
+        retval = connection_finished_flushing(conn); /* ...and get handled here. */
+      }
+    }
+
+    if(retval < 0) { /* this connection is broken. remove it. */
+      log(LOG_DEBUG,"check_conn_write(): Connection broken, removing.");
+      connection_remove(conn);
+      connection_free(conn);
+      if(i<nfds) { /* we just replaced the one at i with a new one.
+                      process it too. */
+        check_conn_read(i);
+      }
+    }
+  }
+}
+
+void check_conn_marked(int i) {
+  connection_t *conn;
+
+  conn = connection_array[i];
+  assert(conn);
+  if(conn->marked_for_close) {
+    log(LOG_DEBUG,"check_conn_marked(): Cleaning up connection.");
+    connection_flush_buf(conn); /* flush it first */
+    connection_remove(conn);
+    connection_free(conn);
+    if(i<nfds) { /* we just replaced the one at i with a new one.
+                    process it too. */
+      check_conn_read(i);
+    }
+  }
+}
+
+#if 0
+void check_conn_hup(int i) {
+  connection_t *conn;
+
+  if(poll_array[i].revents & POLLHUP) { /* they've hung up */
+    conn = connection_array[i];
+    log(LOG_DEBUG,"check_conn_hup(): socket %d has hung up.",conn->s);
+    connection_remove(conn);
+    connection_free(conn);
+    
+    if(i<nfds) { /* we just replaced the one at i with a new one.
+                    process it too. */
+      check_conn_hup(i);
+    }
+  }
+}
+#endif
+
+int do_main_loop(void) {
+
+  int i;
+
+  /* load the routers file */
+  router_array = getrouters(options[RouterFile].r.str,&rarray_len);
+  if (!router_array)
+  {
+    log(LOG_ERR,"Error loading router list.");
+    exit(1);
+  }
+
+  /* load the private key */
+  ERR_load_crypto_strings();
+  prkey = load_prkey(options[PrivateKeyFile].r.str);
+  if (!prkey)
+  {
+    log(LOG_ERR,"Error loading private key.");
+    exit(1);
+  }
+  log(LOG_DEBUG,"core : Loaded private key of size %u bytes.",RSA_size(prkey));
+  ERR_free_strings();
+
+  /* try to connect to all the other ORs, and start the listeners */
+  retry_all_connections(router_array, rarray_len, prkey, 
+		        options[NetworkPort].r.i,options[EntryPort].r.i);
+
+  for(;;) {
+    poll(poll_array, nfds, -1); /* poll until we have an event */
+
+    /* do all the reads first, so we can detect closed sockets */
+    for(i=0;i<nfds;i++)
+      check_conn_read(i);
+
+    /* then do the writes */
+    for(i=0;i<nfds;i++)
+      check_conn_write(i);
+
+    /* any of the conns need to be closed now? */
+    for(i=0;i<nfds;i++)
+      check_conn_marked(i); 
+
+#if 0 /* no, check_conn_read() takes care of hups. */
+    /* remove the ones that have disconnected */
+    for(i=0;i<nfds;i++)
+      check_conn_hup(i);
+#endif
+  }
+
+}
+
+void catch ()
+{
+  errno = 0; /* netcat does this. it looks fun. */
+
+  log(LOG_DEBUG,"Catching ^c, exiting cleanly.");
+   
+  exit(0);
+  
+}
+
+int main(int argc, char *argv[])
+{
+  int retval = 0;
+
+  char *conf_filename = NULL; /* configuration file */
+  signal (SIGINT, catch); /* to catch ^c so we can exit cleanly */
+
+  /* get command-line arguments */
+  retval = getargs(argc,argv,args,&conf_filename,&loglevel);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Error processing command-line arguments.");
+    exit(1);
+  }
+
+  /* load config file */
+  retval = getconfig(conf_filename,options);
+  if (retval == -1)
+  {
+    log(LOG_ERR,"Error loading configuration file.");
+    exit(1);
+  }
+  else if (options[RouterFile].err != 1)
+  { 
+    log(LOG_ERR,"RouterFile option required, but not found.");
+    exit(1);
+  }
+  else if (options[PrivateKeyFile].err != 1)
+  { 
+    log(LOG_ERR,"PrivateKeyFile option required but not found.");
+    exit(1);
+  }
+  else if (options[EntryPort].err != 1)
+  { 
+    log(LOG_ERR,"EntryPort option required but not found.");
+    exit(1);
+  }
+  else if (options[NetworkPort].err != 1)
+  { 
+    log(LOG_ERR,"NetworkPort option required but not found.");
+    exit(1);
+  }
+  else if (options[MaxConn].err != 1)
+  { 
+    log(LOG_ERR,"MaxConn option required but not found.");
+    exit(1);
+  }
+#if 0
+  else if (options[MaxConnTimeout].err != 1)
+  { 
+    conn_tout.tv_sec = OR_DEFAULT_CONN_TIMEOUT;
+  }
+  else
+  { 
+    if (!options[MaxConnTimeout].r.i)
+      conn_toutp = NULL;
+    else
+      conn_tout.tv_sec = options[MaxConnTimeout].r.i;
+  }
+  conn_tout.tv_usec = 0;
+
+  if (!options[TrafficShaping].err)
+  { 
+    options[TrafficShaping].r.i = DEFAULT_POLICY;
+  }
+  else if ((options[TrafficShaping].r.i < 0) || (options[TrafficShaping].r.i > 1))
+  {
+    log(LOG_ERR,"Invalid value for the TrafficShaping option.");
+    exit(1);
+  }
+#endif
+
+  ERR_load_crypto_strings();
+  retval = do_main_loop();
+  ERR_free_strings();
+
+  return retval;
+
+}
+

+ 62 - 0
src/or/onion.c

@@ -0,0 +1,62 @@
+
+#include "or.h"
+
+/********* START VARIABLES **********/
+
+tracked_onion_t *tracked_onions = NULL; /* linked list of tracked onions */
+tracked_onion_t *last_tracked_onion = NULL;
+
+/********* END VARIABLES ************/
+
+
+int decide_aci_type(uint32_t local_addr, uint16_t local_port,
+                    uint32_t remote_addr, uint16_t remote_port) {
+
+  if(local_addr > remote_addr)
+    return ACI_TYPE_HIGHER;
+  if(local_addr < remote_addr)
+    return ACI_TYPE_LOWER;
+  if(local_port > remote_port)
+    return ACI_TYPE_HIGHER;
+   /* else */
+   return ACI_TYPE_LOWER; 
+}
+
+int process_onion(circuit_t *circ, connection_t *conn) {
+  aci_t aci_type;
+
+  if(!decrypt_onion((onion_layer_t *)circ->onion,circ->onionlen,conn->prkey)) {
+    log(LOG_DEBUG,"command_process_create_cell(): decrypt_onion() failed, closing circuit.");
+    return -1;
+  }
+  log(LOG_DEBUG,"command_process_create_cell(): Onion decrypted.");
+
+  /* check freshness */
+  if (((onion_layer_t *)circ->onion)->expire < time(NULL)) /* expired onion */
+  { 
+    log(LOG_NOTICE,"I have just received an expired onion. This could be a replay attack.");
+    return -1;
+  }
+
+  aci_type = decide_aci_type(conn->local.sin_addr.s_addr, conn->local.sin_port,
+             ((onion_layer_t *)circ->onion)->addr,((onion_layer_t *)circ->onion)->port);
+      
+  if(circuit_init(circ, aci_type) < 0) { 
+    log(LOG_ERR,"process_onion(): init_circuit() failed.");
+    return -1;
+  }
+
+  /* check for replay */
+  if(id_tracked_onion(circ->onion, circ->onionlen, tracked_onions)) {
+    log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack.");
+    return -1;
+  }
+
+  /* track the new onion */
+  if(!new_tracked_onion(circ->onion,circ->onionlen, &tracked_onions, &last_tracked_onion)) {
+    log(LOG_DEBUG,"process_onion(): Onion tracking failed. Will ignore.");
+  }
+
+  return 0;
+}
+

+ 411 - 0
src/or/or.h

@@ -0,0 +1,411 @@
+/* Copyright (c) 2002 Roger Dingledine.  See LICENSE for licensing information */
+/* $Id$ */
+
+#ifndef __OR_H
+#define __OR_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include "../common/cell.h"
+#include "../common/config.h"
+#include "../common/key.h"
+#include "../common/log.h"
+#include "../common/onion.h"
+#include "../common/ss.h"
+
+#define MAXCONNECTIONS 200 /* upper bound on max connections.
+			      can be overridden by config file */
+
+#define MAX_BUF_SIZE (64*1024)
+
+#define ACI_TYPE_LOWER 0
+#define ACI_TYPE_HIGHER 1
+
+#define CONN_TYPE_OP_LISTENER 1
+#define CONN_TYPE_OP 2
+#define CONN_TYPE_OR_LISTENER 3
+#define CONN_TYPE_OR 4
+#define CONN_TYPE_APP 5
+
+#define LISTENER_STATE_READY 0
+
+#define OP_CONN_STATE_AWAITING_KEYS 0
+#define OP_CONN_STATE_OPEN 1
+#if 0
+#define OP_CONN_STATE_CLOSE 2 /* flushing the buffer, then will close */
+#define OP_CONN_STATE_CLOSE_WAIT 3 /* have sent a destroy, awaiting a confirmation */
+#endif
+
+#define OR_CONN_STATE_CLIENT_CONNECTING 0
+#define OR_CONN_STATE_CLIENT_SENDING_AUTH 1 /* sending address and info */
+#define OR_CONN_STATE_CLIENT_AUTH_WAIT 2 /* have sent address and info, waiting */
+#define OR_CONN_STATE_CLIENT_SENDING_NONCE 3 /* sending nonce, last piece of handshake */
+#define OR_CONN_STATE_SERVER_AUTH_WAIT 4 /* waiting for address and info */
+#define OR_CONN_STATE_SERVER_SENDING_AUTH 5 /* writing auth and nonce */
+#define OR_CONN_STATE_SERVER_NONCE_WAIT 6 /* waiting for confirmation of nonce */
+#define OR_CONN_STATE_OPEN 7 /* ready to send/receive cells. */
+
+#define APP_CONN_STATE_CONNECTING_WAIT 0 /* waiting for standard structure or dest info */
+#define APP_CONN_STATE_CONNECTING 1
+#define APP_CONN_STATE_OPEN 2
+#if 0
+#define APP_CONN_STATE_CLOSE 3 /* flushing the buffer, then will close */
+#define APP_CONN_STATE_CLOSE_WAIT 4 /* have sent a destroy, awaiting a confirmation */
+#endif
+
+#define CIRCUIT_STATE_OPEN_WAIT 0 /* receiving/processing the onion */
+#define CIRCUIT_STATE_OPEN 1 /* onion processed, ready to send data along the connection */
+#define CIRCUIT_STATE_CLOSE_WAIT1 2 /* sent two "destroy" signals, waiting for acks */
+#define CIRCUIT_STATE_CLOSE_WAIT2 3 /* received one ack, waiting for one more 
+				       (or if just one was sent, waiting for that one */
+//#define CIRCUIT_STATE_CLOSE 4 /* both acks received, connection is dead */ /* NOT USED */
+
+typedef uint16_t aci_t;
+
+typedef struct
+{ 
+
+/* Used by all types: */
+
+  unsigned char type;
+  int state;
+  int s; /* our socket */
+  int poll_index;
+  int marked_for_close;
+
+  char *inbuf;
+  size_t inbuflen;
+  size_t inbuf_datalen;
+  int inbuf_reached_eof;
+
+  char *outbuf;
+  size_t outbuflen;
+  size_t outbuf_datalen;
+
+/* used by OP and App: */
+
+  uint16_t aci; /* anonymous connection identifier */
+
+/* used by OR and OP: */
+
+  uint32_t bandwidth; /* connection bandwidth */
+  int window_sent; /* how many cells can i still send? */
+  int window_received; /* how many cells do i still expect to receive? */
+
+  /* link encryption */
+  unsigned char f_session_key[8];
+  unsigned char b_session_key[8];
+  unsigned char f_session_iv[8];
+  unsigned char b_session_iv[8];
+  EVP_CIPHER_CTX f_ctx;
+  EVP_CIPHER_CTX b_ctx;
+
+//  struct timeval lastsend; /* time of last transmission to the client */
+//  struct timeval interval; /* transmission interval */
+
+  uint32_t addr; /* these two uniquely identify a router */
+  uint16_t port;
+
+/* used by app: */
+
+  ss_t ss; /* standard structure */
+  int ss_received; /* size of ss, received so far */
+  
+/* used by OR, to keep state while connect()ing: Kludge. */
+
+  RSA *prkey;
+  struct sockaddr_in local;
+
+   /* link info */
+  uint32_t min;
+  uint32_t max;
+
+  char *address; /* strdup into this, gets free_connection frees it */
+  RSA *pkey; /* public RSA key for the other side */
+
+  char nonce[8];
+ 
+} connection_t;
+
+/* config stuff we know about the other ORs in the network */
+typedef struct
+{
+  char *address;
+ 
+  uint32_t addr;
+  uint16_t port;
+ 
+  RSA *pkey; /* public RSA key */
+ 
+  /* link info */
+  uint32_t min;
+  uint32_t max;
+//  struct timeval  min_interval;
+ 
+  /* time when last data was sent to that router */
+//  struct timeval lastsend;
+ 
+  /* socket */
+//  int s;
+
+  void *next;
+} routerinfo_t;
+
+/* per-anonymous-connection struct */
+typedef struct
+{
+#if 0
+  uint32_t p_addr; /* all in network order */
+  uint16_t p_port;
+#endif
+  uint32_t n_addr;
+  uint16_t n_port;
+  connection_t *p_conn;
+  connection_t *n_conn;
+
+  aci_t p_aci; /* connection identifiers */
+  aci_t n_aci;
+
+  unsigned char p_f; /* crypto functions */
+  unsigned char n_f;
+
+  unsigned char p_key[128]; /* crypto keys */
+  unsigned char n_key[128];
+
+  unsigned char p_iv[16]; /* initialization vectors */
+  unsigned char n_iv[16];
+
+  EVP_CIPHER_CTX p_ctx; /* cipher context */
+  EVP_CIPHER_CTX n_ctx;
+
+  uint32_t expire; /* expiration time for the corresponding onion */
+
+  int state;
+
+  unsigned char *onion; /* stores the onion when state is CONN_STATE_OPEN_WAIT */
+  uint32_t onionlen; /* total onion length */
+  uint32_t recvlen; /* length of the onion so far */
+
+  void *next;
+} circuit_t;
+
+
+
+
+    /* all the function prototypes go here */
+
+
+/********************************* args.c ***************************/
+
+/* print help*/
+void print_usage();
+
+/* get command-line arguments */
+int getargs(int argc,char *argv[], char *args,char **conf_filename, int *loglevel);
+
+/********************************* buffers.c ***************************/
+
+int buf_new(char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+
+int buf_free(char *buf);
+
+int read_to_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen, int *preached_eof);
+  /* grab from s, put onto buf, return how many bytes read */
+
+int flush_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+  /* push from buf onto s
+   * then memmove to front of buf
+   * return -1 or how many bytes remain on the buf */
+
+int write_to_buf(char *string, size_t string_len,
+                 char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+  /* append string to buf (growing as needed, return -1 if "too big")
+   * return total number of bytes on the buf
+   */
+
+int fetch_from_buf(char *string, size_t string_len,
+		                 char **pbuf, size_t *pbuflen, size_t *pbuf_datalen);
+	  /* if there is string_len bytes in buf, write them onto string,
+	  *    * then memmove buf back (that is, remove them from buf) */
+
+/********************************* cell.c ***************************/
+
+int check_sane_cell(cell_t *cell);
+
+/********************************* circuit.c ***************************/
+
+void circuit_add(circuit_t *circ);
+void circuit_remove(circuit_t *circ);
+
+circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn);
+
+/* internal */
+aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type);
+
+circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn);
+circuit_t *circuit_get_by_conn(connection_t *conn);
+
+int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type);
+int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type);
+
+int circuit_init(circuit_t *circ, int aci_type);
+void circuit_free(circuit_t *circ);
+
+void circuit_close(circuit_t *circ);
+
+void circuit_about_to_close_connection(connection_t *conn);
+  /* flush and send destroys for all circuits using conn */
+
+/********************************* command.c ***************************/
+
+void command_process_cell(cell_t *cell, connection_t *conn);
+
+void command_process_create_cell(cell_t *cell, connection_t *conn);
+void command_process_data_cell(cell_t *cell, connection_t *conn);
+void command_process_destroy_cell(cell_t *cell, connection_t *conn);
+
+/********************************* config.c ***************************/
+
+/* loads the configuration file */
+int getconfig(char *filename, config_opt_t *options);
+
+/********************************* connection.c ***************************/
+
+connection_t *connection_new(int type);
+
+void connection_free(connection_t *conn);
+
+int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type);
+
+int connection_handle_listener_read(connection_t *conn, int new_type, int new_state);
+
+/* start all connections that should be up but aren't */
+int retry_all_connections(routerinfo_t **router_array, int rarray_len,
+		  RSA *prkey, uint16_t or_port, uint16_t op_port);
+
+int connection_read_to_buf(connection_t *conn);
+
+int connection_fetch_from_buf(char *string, int len, connection_t *conn);
+
+int connection_flush_buf(connection_t *conn);
+
+int connection_write_to_buf(char *string, int len, connection_t *conn);
+int connection_send_destroy(aci_t aci, connection_t *conn);
+int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn);
+int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn);
+
+int connection_process_inbuf(connection_t *conn);
+int connection_process_cell_from_inbuf(connection_t *conn);
+
+int connection_finished_flushing(connection_t *conn);
+
+/********************************* connection_or.c ***************************/
+
+int connection_or_process_inbuf(connection_t *conn);
+int connection_or_finished_flushing(connection_t *conn);
+
+connection_t *connection_or_new(void);
+connection_t *connection_or_listener_new(void);
+
+void conn_or_init_crypto(connection_t *conn);
+
+int or_handshake_client_process_auth(connection_t *conn);
+int or_handshake_client_send_auth(connection_t *conn);
+
+int or_handshake_server_process_auth(connection_t *conn);
+int or_handshake_server_process_nonce(connection_t *conn);
+
+int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local);
+
+int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local);
+int connection_or_handle_listener_read(connection_t *conn);
+
+/********************************* connection_op.c ***************************/
+
+connection_t *connection_op_new(void);
+connection_t *connection_op_listener_new(void);
+
+int op_handshake_process_keys(connection_t *conn);
+
+int connection_op_process_inbuf(connection_t *conn);
+
+int connection_op_finished_flushing(connection_t *conn);
+
+int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local);
+
+int connection_op_handle_listener_read(connection_t *conn);
+
+/********************************* connection_app.c ***************************/
+
+connection_t *connection_app_new(void);
+
+int connection_app_process_inbuf(connection_t *conn);
+int connection_app_package_inbuf(connection_t *conn);
+int connection_app_process_data_cell(cell_t *cell, connection_t *conn);
+
+int connection_app_finished_flushing(connection_t *conn);
+
+/********************************* main.c ***************************/
+
+int connection_add(connection_t *conn);
+int connection_remove(connection_t *conn);
+void connection_set_poll_socket(connection_t *conn);
+
+connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port);
+
+connection_t *connection_get_by_type(int type);
+
+routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
+
+void connection_watch_events(connection_t *conn, short events);
+
+void check_conn_read(int i);
+void check_conn_marked(int i);
+void check_conn_write(int i);
+
+void check_conn_hup(int i);
+
+int do_main_loop(void);
+
+int main(int argc, char *argv[]);
+
+/********************************* onion.c ***************************/
+
+int decide_aci_type(uint32_t local_addr, uint16_t local_port,
+                    uint32_t remote_addr, uint16_t remote_port);
+
+int process_onion(circuit_t *circ, connection_t *conn);
+
+/********************************* routers.c ***************************/
+
+routerinfo_t **getrouters(char *routerfile, size_t *listlenp);
+void delete_routerlist(routerinfo_t *list);
+/* create an NULL-terminated array of pointers pointing to elements of a router list */
+routerinfo_t **make_rarray(routerinfo_t* list, size_t *listlenp);
+
+
+#endif

+ 365 - 0
src/or/routers.c

@@ -0,0 +1,365 @@
+/**
+ * routers.c 
+ * Routines for loading the list of routers and their public RSA keys.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+#define OR_ROUTERLIST_SEPCHARS " \t\n"
+#define OR_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n"
+
+#include "or.h"
+
+/* delete a list of routers from memory */
+void delete_routerlist(routerinfo_t *list)
+{
+  routerinfo_t *tmp = NULL;
+  
+  if (!list)
+    return;
+  
+  do
+  {
+    tmp=list->next;
+    free((void *)list->address);
+    RSA_free(list->pkey);
+    free((void *)list);
+    list = tmp;
+  }
+  while (list != NULL);
+  
+  return;
+}
+
+/* create an NULL-terminated array of pointers pointing to elements of a router list */
+/* this is done in two passes through the list - inefficient but irrelevant as this is
+ * only done once when op/or start up */
+routerinfo_t **make_rarray(routerinfo_t* list, size_t *len)
+{
+  routerinfo_t *tmp=NULL;
+  int listlen = 0;
+  routerinfo_t **array=NULL;
+  routerinfo_t **p=NULL;
+  
+  if ((!list) || (!len))
+    return NULL;
+  
+  /* get the length of the list */
+  tmp = list;
+  do
+  {
+    listlen++;
+    tmp = tmp->next;
+  }
+  while (tmp != NULL);
+  
+  array = malloc((listlen+1)*sizeof(routerinfo_t *));
+  if (!array)
+  {
+    log(LOG_ERR,"Error allocating memory.");
+    return NULL;
+  }
+  
+  tmp=list;
+  p = array;
+  do
+  {
+    *p = tmp;
+    p++;
+    tmp = tmp->next;
+  }
+  while(tmp != NULL);
+  *p=NULL;
+  
+  *len = listlen;
+  return array;
+}
+
+/* load the router list */
+routerinfo_t **getrouters(char *routerfile, size_t *lenp)
+{
+  int retval = 0;
+  char *retp = NULL;
+  routerinfo_t *router=NULL, *routerlist=NULL, *lastrouter=NULL;
+  FILE *rf; /* router file */
+  fpos_t fpos;
+  char line[512];
+  char *token;
+  char *errtest; /* detecting errors in strtoul() calls */
+  struct hostent *rent;
+
+  if ((!routerfile) || (!lenp))
+    return NULL;
+  
+  if (strcspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0)
+  {
+    log(LOG_ERR,"Filename %s contains illegal characters.",routerfile);
+    return NULL;
+  }
+  
+  /* open the router list */
+  rf = fopen(routerfile,"r");
+  if (!rf)
+  {
+    log(LOG_ERR,"Could not open %s.",routerfile);
+    return NULL;
+  }
+  
+  retp= fgets(line,512,rf);
+  while (retp)
+  {
+    log(LOG_DEBUG,"getrouters():Line :%s",line);
+    token = (char *)strtok(line,OR_ROUTERLIST_SEPCHARS);
+    if (token)
+    {
+      log(LOG_DEBUG,"getrouters():Token : %s",token);
+      if (token[0] != '#') /* ignore comment lines */
+      {
+	router = malloc(sizeof(routerinfo_t));
+	if (!router)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  fclose(rf);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+	
+#if 0
+	router->conn_bufs = NULL; /* no output buffers */
+	router->last_conn_buf = NULL;
+	router->next_to_service = 0;
+	
+	router->s = -1; /* to signify this router is as yet unconnected */
+	router->celllen = 0; /* cell buffer is empty */
+#endif
+	
+	/* read the address */
+	router->address = malloc(strlen(token)+1);
+	if (!router->address)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  fclose(rf);
+	  free((void *)router);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+	strcpy(router->address,token);
+	
+	rent = (struct hostent *)gethostbyname(router->address);
+	if (!rent)
+	{
+	  log(LOG_ERR,"Could not get address for router %s.",router->address);
+	  fclose(rf);
+	  free((void *)router->address);
+	  free((void *)router);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+
+	memcpy(&router->addr, rent->h_addr,rent->h_length);
+	
+	/* read the port */
+	token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
+	if (token)
+	{
+	  log(LOG_DEBUG,"getrouters():Token :%s",token);
+	  router->port = (uint16_t)strtoul(token,&errtest,0);
+	  if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
+	  {
+	    /* convert port to network format */
+	    router->port = htons(router->port);
+	    
+	    /* read min bandwidth */
+	    token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
+	    if (token) /* min bandwidth */
+	    {
+	      router->min = (uint32_t)strtoul(token,&errtest,0);
+	      if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
+	      {
+		if (router->min) /* must not be zero */
+		{
+		  /* read max bandwidth */
+		  token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
+		  if (token) /* max bandwidth */
+		  {
+		    router->max = (uint32_t)strtoul(token,&errtest,0);
+		    if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
+		    {
+		      if (router->max) /* must not be zero */
+		      {
+			/* check that there is a public key entry for that router */
+			retval = fgetpos(rf, &fpos); /* save the current file position
+						      * we wil return to it later if we find a public key */
+			if (retval == -1)
+			{
+			  log(LOG_ERR,"Could not save position in %s.",routerfile);
+			  free((void *)router->address);
+			  free((void *)router);
+			  fclose(rf);
+			  delete_routerlist(routerlist);
+			  return NULL;
+			}
+			do /* read through to the next non-empty line */
+			{
+			  retp=fgets(line,512,rf);
+			  if (!retp)
+			  {
+			    log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
+			    free((void *)router->address);
+			    free((void *)router);
+			    fclose(rf);
+			    delete_routerlist(routerlist);
+			    return NULL;
+			  }
+			  log(LOG_DEBUG,"getrouters():Line:%s",line);
+			  if ((*line != '#') && ( strspn(line,OR_ROUTERLIST_SEPCHARS) != strlen(line) ))
+			  {
+			    break;
+			  }
+			} while (1);
+			
+			if (!strcmp(line,OR_PUBLICKEY_BEGIN_TAG)) /* we've got the public key */
+			{
+			  retval = fsetpos(rf,&fpos); /* get us back to where we were otherwise crypto lib won't find the key */
+			  if (retval == -1)
+			  {
+			    log(LOG_ERR,"Could not set position in %s.",routerfile);
+			    free((void *)router->address);
+			    free((void *)router);
+			    fclose(rf);
+			    delete_routerlist(routerlist);
+			    return NULL;
+			  }
+			}
+			else /* we found something else; this isn't right */
+			{
+			  log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
+			  free((void *)router->address);
+			  free((void *)router);
+			  fclose(rf);
+			  delete_routerlist(routerlist);
+			  return NULL;
+			}
+			
+			log(LOG_DEBUG,"getrouters():Reading the key ...");
+			/* read the public key into router->pkey */
+			router->pkey=NULL;
+			router->pkey = PEM_read_RSAPublicKey(rf,&router->pkey,NULL,NULL);
+			if (!router->pkey) /* something went wrong */
+			{
+			  log(LOG_ERR,"Could not read public key for router %s:%u.",router->address,router->port);
+			  free((void *)router->address);
+			  free((void *)router);
+			  fclose(rf);
+			  delete_routerlist(routerlist);
+			  return NULL;
+			}
+			else /* read the key */
+			{
+			  log(LOG_DEBUG,"getrouters():Public key size = %u.", RSA_size(router->pkey));
+			  if (RSA_size(router->pkey) != 128) /* keys MUST be 1024 bits in size */
+			  {
+			    log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->port);
+			    free((void *)router->address);
+			    RSA_free(router->pkey);
+			    free((void *)router);
+			    fclose(rf);
+			    delete_routerlist(routerlist);
+			    return NULL;
+			  }
+			  router->next = NULL;
+			  /* save the entry into the routerlist linked list */
+			  if (!routerlist) /* this is the first entry */
+			    routerlist = router;
+			  else
+			    lastrouter->next = (void *)router;
+			  lastrouter = router;
+			}
+		      }
+		      else /* maximum link utilisation is zero */
+		      {
+			log(LOG_ERR,"Entry for router %s doesn't contain a valid maximum bandwidth entry (must be > 0).",router->address);
+			free((void *)router->address);
+			free((void *)router);
+			fclose(rf);
+			delete_routerlist(routerlist);
+			return NULL;
+		      }
+		    }
+		    else
+		    {
+		      log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid maximum bandwidth entry.",router->address);
+		      free((void *)router->address);
+		      free((void *)router);
+		      fclose(rf);
+		      delete_routerlist(routerlist);
+		      return NULL;
+		    }
+		  }
+		  else
+		  {
+		    log(LOG_ERR,"Entry for router %s doesn't seem to contain a maximum bandwidth entry.",router->address);
+		    free((void *)router->address);
+		    free((void *)router);
+		    fclose(rf);
+		    delete_routerlist(routerlist);
+		    return NULL;
+		  }
+		}
+		else
+		{
+		  log(LOG_ERR,"Entry for router %s doesn't contain a valid minimum bandwidth entry (must be > 0).",router->address);
+		  free((void *)router->address);
+		  free((void *)router);
+		  fclose(rf);
+		  delete_routerlist(routerlist);
+		  return NULL;
+		}
+	      }
+	      else
+	      {
+		log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid minimum bandwidth entry.",router->address);
+		free((void *)router->address);
+		free((void *)router);
+		fclose(rf);
+		delete_routerlist(routerlist);
+		return NULL;
+	      }
+	    }
+	    else
+	    {
+	      log(LOG_ERR,"Entry for router %s doesn't seem to contain a minimum bandwidth entry.",router->address);
+	      free((void *)router->address);
+	      free((void *)router);
+	      fclose(rf);
+	      delete_routerlist(routerlist);
+	      return NULL;
+	    }
+	  }
+	  else
+	  {
+	    log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid port number.",router->address);
+	    free((void *)router->address);
+	    free((void *)router);
+	    fclose(rf);
+	    delete_routerlist(routerlist);
+	    return NULL;
+	  }
+	}
+	else
+	{
+	  log(LOG_ERR,"Entry for router %s doesn't seem to contain a port number.",router->address);
+	  free((void *)router->address);
+	  free((void *)router);
+	  fclose(rf);
+	  delete_routerlist(routerlist);
+	  return NULL;
+	}
+      }
+    }
+    retp=fgets(line,512,rf);
+  }
+  
+  fclose(rf);
+  return make_rarray(routerlist, lenp);
+}

+ 58 - 0
src/orkeygen/Makefile

@@ -0,0 +1,58 @@
+SRC=orkeygen.c
+OBJ=${SRC:.c=.o}
+PROGS=orkeygen
+LIB=-L/usr/local/ssl/lib
+LIBS=-lcrypto
+INCLUDE=-I/usr/local/ssl/include
+
+CFLAGS= $(INCLUDE)
+LDFLAGS = $(LIB) ${LIBS}
+
+all:	${PROGS}
+
+clean:
+	rm -f *.o ${PROGS}
+
+depend:
+	makedepend -- ${CFLAGS} -- ${SRC}
+
+${PROGS}:	${OBJ}
+	$(LINK.c) -o $@ $(OBJ)
+
+# DO NOT DELETE
+
+
+orkeygen.o: /usr/include/alloca.h /usr/include/asm/errno.h
+orkeygen.o: /usr/include/bits/endian.h /usr/include/bits/errno.h
+orkeygen.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h
+orkeygen.o: /usr/include/bits/select.h /usr/include/bits/sigset.h
+orkeygen.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+orkeygen.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h
+orkeygen.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+orkeygen.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h
+orkeygen.o: /usr/include/_G_config.h /usr/include/gconv.h
+orkeygen.o: /usr/include/gnu/stubs.h /usr/include/libio.h
+orkeygen.o: /usr/include/linux/errno.h /usr/include/openssl/asn1.h
+orkeygen.o: /usr/include/openssl/bio.h /usr/include/openssl/blowfish.h
+orkeygen.o: /usr/include/openssl/bn.h /usr/include/openssl/buffer.h
+orkeygen.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h
+orkeygen.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h
+orkeygen.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h
+orkeygen.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h
+orkeygen.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h
+orkeygen.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h
+orkeygen.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h
+orkeygen.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h
+orkeygen.o: /usr/include/openssl/pem2.h /usr/include/openssl/pem.h
+orkeygen.o: /usr/include/openssl/pkcs7.h /usr/include/openssl/rc2.h
+orkeygen.o: /usr/include/openssl/rc4.h /usr/include/openssl/ripemd.h
+orkeygen.o: /usr/include/openssl/rsa.h /usr/include/openssl/safestack.h
+orkeygen.o: /usr/include/openssl/sha.h /usr/include/openssl/stack.h
+orkeygen.o: /usr/include/openssl/symhacks.h /usr/include/openssl/x509.h
+orkeygen.o: /usr/include/openssl/x509_vfy.h /usr/include/stdio.h
+orkeygen.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h
+orkeygen.o: /usr/include/sys/select.h /usr/include/sys/sysmacros.h
+orkeygen.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/wchar.h
+orkeygen.o: /usr/include/xlocale.h
+orkeygen.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+orkeygen.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h

+ 119 - 0
src/orkeygen/orkeygen.c

@@ -0,0 +1,119 @@
+/**
+ * orkeygen.c 
+ * Key generation utility. 
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.1  2002/01/04 07:19:27  badbytes
+ * Key generation utility.
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+int main(int argc, char *argv[])
+{
+
+  char *file_pr = argv[1];
+  char *file_pu = argv[2];
+  
+  FILE *f_pr = NULL;
+  FILE *f_pu = NULL;
+  
+  RSA *rsa_key = NULL;
+ 
+  int retval = 0;
+  
+  
+  if (argc < 3)
+  {
+    printf("Need two files, for private and public key in that order.\n");
+    exit(1);
+  }
+  
+  /* generate the key */
+  rsa_key = RSA_generate_key(1024,65535,NULL,NULL);
+  if (!rsa_key) /* error has occured */
+  {
+    printf("%s",ERR_reason_error_string(ERR_get_error()));
+    exit(1);
+  }
+  else /* keys generated */
+  {
+    retval = RSA_check_key(rsa_key);
+    if (retval == 1)
+    {
+      printf("Generated key seems to be valid.\n");
+      /* open the output files */
+      f_pr = fopen(file_pr,"w");
+      if (!f_pr)
+      {
+	perror("fopen");
+	RSA_free(rsa_key);
+	exit(1);
+      }
+      
+      f_pu = fopen(file_pu,"w");
+      if (!f_pu)
+      {
+	perror("fopen");
+	RSA_free(rsa_key);
+	exit(1);
+      }
+      
+      /* write the private key */
+      retval = PEM_write_RSAPrivateKey(f_pr,rsa_key,NULL,NULL,0,0,NULL);
+      if (retval == 0)
+      {
+	printf("%s",ERR_reason_error_string(ERR_get_error()));
+	fclose(f_pr);
+	fclose(f_pu);
+	RSA_free(rsa_key);
+	exit(1);
+      }
+      
+      /* write the public key */
+      retval = PEM_write_RSAPublicKey(f_pu,rsa_key);
+      if (retval == 0)
+      {
+	printf("%s",ERR_reason_error_string(ERR_get_error()));
+	fclose(f_pr);
+	fclose(f_pu);
+	RSA_free(rsa_key);
+	exit(1);
+      }
+      
+      printf("Keys written to files %s (public) and %s (private).\n",file_pu,file_pr);
+    }
+    else if (retval == 0)
+    {
+      printf("Generated key seems to be invalid. Exiting.\n");
+      RSA_free(rsa_key);
+      exit(1);
+    }
+    else if (retval == -1)
+    {
+      printf("%s",ERR_reason_error_string(ERR_get_error()));
+      RSA_free(rsa_key);
+      exit(1);
+    }
+  }
+     
+  RSA_free(rsa_key);
+  fclose(f_pu);
+  fclose(f_pr);
+  exit(0);
+  
+ }

+ 80 - 0
src/smtpap/Makefile

@@ -0,0 +1,80 @@
+SRC=smtpap.c io.c
+OBJ=${SRC:.c=.o}
+PROGS=smtpap
+LIB=
+LIBS=
+INCLUDE =
+
+CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb
+LDFLAGS = $(LIB) $(LIBS)
+
+all:	${PROGS}
+
+clean:
+	rm -f *.o ${PROGS}
+
+depend:
+	makedepend -- ${CFLAGS} -- ${SRC}
+
+${PROGS}:	${OBJ}
+	$(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/utils.o
+
+# DO NOT DELETE
+
+
+io.o: ../common/log.h ../common/utils.h io.h smtpap.h
+io.o: /usr/include/bits/endian.h /usr/include/bits/pthreadtypes.h
+io.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+io.o: /usr/include/bits/sigset.h /usr/include/bits/sockaddr.h
+io.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h
+io.o: /usr/include/bits/types.h /usr/include/bits/wchar.h
+io.o: /usr/include/endian.h /usr/include/features.h /usr/include/_G_config.h
+io.o: /usr/include/gconv.h /usr/include/gnu/stubs.h /usr/include/libio.h
+io.o: /usr/include/malloc.h /usr/include/stdio.h /usr/include/string.h
+io.o: /usr/include/sys/cdefs.h /usr/include/syslog.h /usr/include/sys/select.h
+io.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h
+io.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/sys/un.h
+io.o: /usr/include/time.h /usr/include/wchar.h /usr/include/xlocale.h
+io.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+io.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+smtpap.o: ../common/config.h ../common/log.h ../common/ss.h ../common/utils.h
+smtpap.o: ../common/version.h io.h smtpap.h /usr/include/alloca.h
+smtpap.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h
+smtpap.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h
+smtpap.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h
+smtpap.o: /usr/include/bits/confname.h /usr/include/bits/endian.h
+smtpap.o: /usr/include/bits/environments.h /usr/include/bits/errno.h
+smtpap.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h
+smtpap.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h
+smtpap.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h
+smtpap.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h
+smtpap.o: /usr/include/bits/sched.h /usr/include/bits/select.h
+smtpap.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h
+smtpap.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h
+smtpap.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h
+smtpap.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h
+smtpap.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h
+smtpap.o: /usr/include/bits/time.h /usr/include/bits/types.h
+smtpap.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h
+smtpap.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h
+smtpap.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h
+smtpap.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h
+smtpap.o: /usr/include/features.h /usr/include/_G_config.h
+smtpap.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h
+smtpap.o: /usr/include/libio.h /usr/include/limits.h
+smtpap.o: /usr/include/linux/errno.h /usr/include/linux/limits.h
+smtpap.o: /usr/include/netdb.h /usr/include/netinet/in.h
+smtpap.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h
+smtpap.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h
+smtpap.o: /usr/include/sys/cdefs.h /usr/include/syslog.h
+smtpap.o: /usr/include/sys/resource.h /usr/include/sys/select.h
+smtpap.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h
+smtpap.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h
+smtpap.o: /usr/include/sys/types.h /usr/include/sys/ucontext.h
+smtpap.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/sys/wait.h
+smtpap.o: /usr/include/time.h /usr/include/ucontext.h /usr/include/unistd.h
+smtpap.o: /usr/include/wait.h /usr/include/wchar.h /usr/include/xlocale.h
+smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h
+smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h
+smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h
+smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h

+ 133 - 0
src/smtpap/io.c

@@ -0,0 +1,133 @@
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <malloc.h>
+
+#include "../common/log.h"
+#include "../common/utils.h"
+
+#include "smtpap.h"
+#include "io.h"
+
+/* connection timeout */
+extern struct timeval *conn_toutp;
+
+/* printf-like function used to send messages to a socket */
+int sendmessage(int s, char *buf, size_t buflen, const char *format, ...)
+{
+  int retval = 0;
+  va_list ap;
+  
+  if (!buf)
+    return -1;
+  
+  va_start(ap,format);
+  retval = vsnprintf(buf,buflen, format, ap);
+  va_end(ap);
+  
+  if (retval < 0) 
+  {
+    log(LOG_DEBUG,"sendmessage() : could not print to buffer");
+    return -1;
+  }
+
+  log(LOG_DEBUG,"sendmessage() : printed this to buffer : %s",buf);
+  
+  retval = write_tout(s,buf,(size_t)retval, conn_toutp);
+  if (retval < 0)
+  {
+    log(LOG_DEBUG,"sendmessage() : could not send");
+    return -1;
+  }
+  
+  return retval;
+}
+
+/* receive a response from the recipient SMTP server into *op_in
+ * Can handle multi-line responses. */
+int receive(int s, char **inbuf,size_t *inbuflen, int flags)
+{
+  int inputlen = 0; /* running total length of the input */
+  int inputerror = 0; /* has an error occured? */
+  int retval = 0; /* used for saving function return values */
+  
+  /* for processing multi-line responses */
+  int i=0;
+  
+  /* storing old values of *inbuf and *inbuflen */
+  char *inbuf_old = NULL;
+  size_t inbuflen_old=0;
+  
+  if ((!inbuf) || (!*inbuf) || (!inbuflen))
+    return -1;
+
+  /* saving old values in case we need to restore them */
+  inbuf_old = *inbuf;
+  inbuflen_old = *inbuflen;
+
+  do
+  {
+    if (inputlen == *inbuflen-1) /* we need to increase the buffer size */
+    {
+      /* increase the size of the buffer */
+      *inbuflen += 512;
+      
+      *inbuf = (char *)realloc(*inbuf,(size_t)*inbuflen);
+      if (!*inbuf)
+      {
+	log(LOG_ERR,"Could not allocate memory.");
+	*inbuf = inbuf_old;
+	*inbuflen = inbuflen_old;
+	inputerror = 1;
+	break;
+      }
+    }
+    
+    retval=read_tout(s,*inbuf+inputlen,(size_t)(*inbuflen-inputlen-1),flags, conn_toutp); /* subtract 1 from inbuflen to leave space for \0 */
+    if (retval <= 0)
+    {
+      log(LOG_ERR,"Error occured while receiving data.");
+      inputerror = 1;
+      break;
+    }
+    else
+    {
+      inputerror = 0;
+      inputlen += retval;
+
+      /* exit clause if we have received CRLF, otherwise we need to keep reading*/
+      /* also keep on reading if it's a multi-line response */
+      if (inputlen >= SMTPAP_CRLF_LEN)
+      {
+	if (!strncmp(*inbuf+inputlen-SMTPAP_CRLF_LEN,SMTPAP_CRLF,SMTPAP_CRLF_LEN)) /* entire line received */
+	{
+	  /* now check wether we should expect more lines */
+	  /* find the <CRLF> sequence which occurs one before last */
+	  for(i=inputlen-SMTPAP_CRLF_LEN-1; i > 0; i--) /* move backwards, start just before the final CRLF */
+	  {
+	    if ((*inbuf)[i] == SMTPAP_LF) /* got a LF */
+	    {
+	      /* check for a CR preceding it */
+	      if ((*inbuf)[i-1] == SMTPAP_CR) /* got a CR */
+		break;
+	    }
+	  }
+	  if (i==0) /* correct the offset if no CRLF found */
+	    i=-1;
+
+	  /* check the 4th character after the <CRLF> to see if it is - or <SP> */
+	  if ((*inbuf)[i+4] != '-') /* no more lines */
+	    break;
+	}
+      }
+    }
+  } while(1); 
+  
+  if (!inputerror)
+  {
+    (*inbuf)[inputlen]=0; /* add the terminating NULL character */
+    return inputlen;
+  }
+
+  return -1;
+}

+ 2 - 0
src/smtpap/io.h

@@ -0,0 +1,2 @@
+int sendmessage(int s, char *buf, size_t buflen, const char *format, ...);
+int receive(int s, char **inbuf,size_t *inbuflen, int flags);

+ 1393 - 0
src/smtpap/smtpap.c

@@ -0,0 +1,1393 @@
+/**
+ * smtpap.c 
+ * SMTP Application Proxy for Onion Routing
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.32  2002/04/02 14:29:49  badbytes
+ * Final finishes.
+ *
+ * Revision 1.31  2002/03/25 08:03:17  badbytes
+ * Added header sanitization.
+ *
+ * Revision 1.30  2002/03/02 23:54:06  mp292
+ * Fixed missing CRLFs at the end of error messages.
+ *
+ * Revision 1.29  2002/01/29 01:00:10  mp292
+ * All network operations are now timeoutable.
+ *
+ * Revision 1.28  2002/01/28 21:38:18  mp292
+ * Fixed bugs in RSET handling. Added Anonimize option which signifies whether
+ * the router should falsify the identity of the sender or not.
+ *
+ * Revision 1.27  2002/01/26 22:45:02  mp292
+ * Now handles SS_ERROR_INVALID_PORT.
+ *
+ * Revision 1.26  2002/01/26 22:33:21  mp292
+ * Removed hard-coded values for onion proxy return codes. Also fixed a bug in
+ * parameter checking.
+ *
+ * Revision 1.25  2002/01/26 21:58:27  mp292
+ * Added some missing parameter checking.
+ *
+ * Revision 1.24  2002/01/26 21:50:17  mp292
+ * Reviewed according to Secure-Programs-HOWTO. Still need to deal with network
+ * timeouts.
+ *
+ * Revision 1.23  2002/01/18 21:07:02  mp292
+ * (a) THe form of HELO is now HELO Anonymous.Smtp.Daemon rather than the real
+ * address. (b) The user *can* now specify a default SMTP daemon to route through
+ * although this is insecure and not recommended.
+ *
+ * Revision 1.22  2002/01/16 23:01:58  mp292
+ * First phase of system testing completed (main functionality).
+ *
+ * Revision 1.21  2002/01/16 17:04:01  mp292
+ * Bug in checking whether incoming connection is local or not.
+ *
+ * Revision 1.20  2002/01/09 09:18:22  badbytes
+ * Now handles EINTR error from accept().
+ *
+ * Revision 1.19  2001/12/19 11:15:27  badbytes
+ * Corrected AF_INET to PF_INET in socket() calls.
+ *
+ * Revision 1.18  2001/12/19 08:36:04  badbytes
+ * Incorrect error checking in recv() calls caused zombies ... fixed
+ *
+ * Revision 1.17  2001/12/18 14:42:46  badbytes
+ * Variable name op_port_str was incorrectly named, changed to dest_port_str
+ *
+ * Revision 1.16  2001/12/18 13:20:16  badbytes
+ * Some error messages did not include a terminating <CRLF>
+ *
+ * Revision 1.15  2001/12/18 12:37:23  badbytes
+ * Found an overflow bug ...
+ *
+ * Revision 1.14  2001/12/18 09:17:31  badbytes
+ * Corrected a spelling mistake in print_usage()
+ *
+ * Revision 1.13  2001/12/14 13:13:24  badbytes
+ * Changed types.h references to ss.h
+ *
+ * Revision 1.12  2001/12/14 09:17:25  badbytes
+ * Moved function stolower(char *str) from smtpap.c to common/utils.c
+ *
+ * Revision 1.11  2001/12/13 13:51:05  badbytes
+ * Fixed a bug in processing command-line parameters.
+ *
+ * Revision 1.10  2001/12/13 13:36:44  badbytes
+ * Now accepts the -l command-line option which specifies the logging threshold.
+ *
+ * Revision 1.9  2001/12/12 16:02:29  badbytes
+ * Testing completed.
+ *
+ * Revision 1.8  2001/12/11 16:30:20  badbytes
+ * Some bugs removed, still testing though.
+ *
+ * Revision 1.7  2001/12/11 14:12:20  badbytes
+ * Onion Proxy connection setup completed. Proceeding to test.
+ *
+ * Revision 1.6  2001/12/11 10:43:21  badbytes
+ * MAIL and RCPT handling completed. Still coding connection to Onion Proxy.
+ *
+ * Revision 1.5  2001/12/10 16:10:35  badbytes
+ * Wrote a tokenize() function to help with parsing input from SMTP clients.
+ *
+ * Revision 1.4  2001/12/07 15:02:43  badbytes
+ * Server setup code completed.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "../common/log.h"
+#include "../common/config.h"
+#include "../common/ss.h"
+#include "../common/utils.h"
+#include "../common/version.h"
+
+#include "smtpap.h"
+#include "io.h"
+
+int loglevel = LOG_ERR;
+struct timeval conn_tout;
+struct timeval *conn_toutp = &conn_tout;
+
+/* valid command-line options */
+static const char *args = "hf:p:l:";
+
+/* valid config file options */
+static config_opt_t options[] =
+{
+  {"OnionProxy", CONFIG_TYPE_INT, {0}, 0},
+  {"MaxConn", CONFIG_TYPE_INT, {0}, 0},
+  {"Anonimize", CONFIG_TYPE_INT, {0}, 0},
+  {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0},
+  {0}
+};
+enum opts {
+  OnionProxy=0,MaxConn, Anonimize, ConnTimeout
+};
+
+/* number of open connections */
+int connections=0;
+
+/* prints help on using smtpap */
+void print_usage()
+{
+  char *program = "smtpap";
+  
+  printf("\n%s - SMTP application proxy for Onion Routing.\nUsage : %s -f config [-p port -l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program);
+}
+
+/* used for reaping zombie processes */
+void sigchld_handler(int s)
+{
+  while (wait(NULL) > 0);
+  connections--;
+}
+
+/* takes the contents of a RCPT command in a null-terminated string and retrieves the address
+ * of the corresponding recipient domain*/
+char *extract_smtp_dest(char *rcptbuf)
+{
+  char *dest_smtp=NULL;
+  char *pos1, *pos2;
+
+  if (!rcptbuf)
+    return NULL;
+  
+  pos1 = (char *)strchr(rcptbuf,'@');
+  if (pos1 == NULL)
+    return NULL;
+  
+  pos2 = (char *)strpbrk(pos1,SMTPAP_PATH_SEPCHARS);
+  if (pos2 == NULL)
+    return NULL;
+  else 
+  {
+    dest_smtp = (char *)malloc((size_t)(pos2-pos1));
+    if (!dest_smtp)
+    {
+      log(LOG_ERR,"Could not allocate memory.");
+      return NULL;
+    }
+    else
+    {
+      strncpy(dest_smtp,pos1+1,(size_t)(pos2-pos1-1));
+      dest_smtp[pos2-pos1-1] = 0;
+    }
+  }
+  
+  return dest_smtp;
+}
+
+/* process a DATA stream and remove any e-mail headers */
+int sanitize_data(unsigned char **buf, int *buflen)
+{
+  unsigned char *offset; /* offset to data after the last header */
+  unsigned char *crlf = NULL;
+  unsigned char *colon = NULL;
+  unsigned char *line;
+  unsigned char *newbuf;
+  int newbuflen;
+  
+  if ((!buf) || (!buflen)) /* invalid parameters */
+    return -1;
+  
+  offset = *buf;
+  line = *buf;
+  /* process the data line by line and discard anything that looks like a header */
+  while(1)
+  {
+    /* find the end of line */
+    crlf = strstr(line, SMTPAP_CRLF);
+    if (crlf)
+    {
+      colon = (unsigned char *)memchr((void *)line,(int)':',crlf-line);
+      if (!colon)
+	break; /* this doesn't seem to be a header, can stop */
+      else
+	offset = crlf + 2; /* discard this line */
+      
+      line = crlf + 2; /* move on to the next line */
+    }
+    else /* no more CRLFs found, end of data */
+      /* NB : there is no need to check the current line at this stage as this will be of the form <CRLF>.<CRLF> */
+      /*      we should never reach this point in the code anyway, the '.' will be trapped as a non-header line in the above code */
+      break;
+  }
+  
+  if (offset != *buf) /* data changed */
+  {
+    newbuflen = *buflen - (offset - *buf);
+    newbuf = (unsigned char *)malloc(newbuflen+1); /* leave space for a terminating NULL character */
+    if (!newbuf) /* malloc() error */
+      return -1;
+    else
+    {
+      /* copy into the new buffer */
+      memcpy((void *)newbuf, (void *)offset, newbuflen);
+      newbuf[newbuflen] = 0;
+      
+      /* replace the old buffer with the new one */
+      free((void *)*buf);
+      *buf = newbuf;
+      *buflen = newbuflen;
+    }
+  }
+  
+  return 0;
+}
+
+/* main logic of smtpap */
+int handle_connection(int s, struct hostent *local, struct sockaddr_in remote, u_short op_port)
+{
+  int retval = 0;
+  int state = 0; /* 0 - start / RSET received
+		  * 1 - connection not local, waiting for QUIT
+		  * 2 - connection local, waiting for HELO/EHLO
+		  * 3 - HELO/EHLO received, waiting for MAIL
+		  * 4 - MAIL received, waiting for RCPT
+		  * 5 - waiting for DATA
+		  * 6 - DATA received, accepting data
+		  *   - data accepted, back to state 3
+		  */
+  int islocal = 0;
+  char *cp;
+  int i=0;
+  char message[512]; /* for storing outgoing messages */
+  char *inbuf = NULL; /* for storing incoming messages */
+  char *token = NULL; /* next token in the incoming message */
+  char *tmpbuf = NULL; /* temporary buffer for copying data */
+  char *mailbuf = NULL; /* storing the MAIL command */
+  char **rcptarray = NULL; /* storing a NULL-terminated array of RCPT commands */
+  char *rcptbuf = NULL; /* storing a single RCPT command */
+  int tmpbuflen = 0; /* length of tmpbuflen in bytes */
+  int inbuflen = 0; /* length of inbuf in bytes */
+  int inputlen = 0; /* length of actual input in bytes */
+  int mailbuflen=0; /* etc ... */
+  int rcptbuflen=0;
+  int inputerror=0; /* error occured when receiving data from the client */
+  
+  /* the following is used for conecting to the SMTP host through the OR network */
+  char *dest_addr_str = NULL; /* for storing the ASCII address of the destination SMTP */
+  int sop=-1; /* socket for connecting to the onion proxy */
+  struct sockaddr_in op_addr; /* stores the address of the onion proxy */
+  ss_t ss; /* standard structure */
+  char dest_port_str[6]; /* ascii representation of the destination port */
+  /* input and output buffers for talking to the onion proxy */
+  char *op_out = NULL;
+  char *op_in = NULL;
+  int op_outlen = 0;
+  int op_inlen = 0;
+  
+  int partial_dataend = 0; /* used for recognising the <CRLF>.<CRLF> sequence that ends the DATA segment */
+
+  if (!local)
+    return -1;
+  
+  log(LOG_DEBUG, "handle_connection() : Local address = %s.", inet_ntoa(*(struct in_addr *)local->h_addr));
+  log(LOG_DEBUG, "handle_connection() : Remote address = %s.", inet_ntoa(remote.sin_addr));
+  
+  /* first check that the connection is from the local host, otherwise reject */
+  if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1"))
+          islocal = 1;
+  for (i=0; (local->h_addr_list[i] != NULL) && (!islocal); i++)
+  {
+    cp = local->h_addr_list[i];
+    log(LOG_DEBUG,"handle_connection() : Checking if connection is from address %s.",inet_ntoa(*(struct in_addr *)cp));
+    if (!memcmp(&remote.sin_addr, cp, sizeof(struct in_addr)))
+      islocal = 1;
+  }
+  
+  if (islocal)
+  {
+    log(LOG_DEBUG,"handle_connection() : Connection seems to be local. Will accept.");
+    state = 2;
+    sendmessage(s, (char *)message, (size_t)512, "220 This is smtpap v1.0 running on %s.%s",local->h_name,SMTPAP_CRLF);
+  }
+  else
+  {
+    log(LOG_DEBUG,"handle_connection() : Connection doesn't seem to be local. Will reject.");
+    state = 1;
+    sendmessage(s,(char *)message, (size_t)512,"554 smtpap v1.0 Connection refused. Only local connections allowed.%s",SMTPAP_CRLF);
+  }
+  
+  /* initially allocate 512 bytes for incoming message buffer */
+  inbuf = (char *)malloc((size_t)512);
+  if (!inbuf)
+  {
+    log(LOG_ERR,"Could not allocate memory.");
+    return -1;
+  }
+  inbuflen = 512;
+
+  /* initially allocate 512 bytes for the temporary buffer */
+  tmpbuf = (char *)malloc((size_t)512);
+  if (!tmpbuf)
+  {
+    log(LOG_ERR,"Could not allocate memory.");
+    free(inbuf);
+    return -1;
+  }
+  tmpbuflen = 512;
+  
+  while(1)
+  {
+    inputlen = 0;
+    do
+    {
+      if (inputlen == inbuflen-1) /* we need to increase the buffer size */
+      {
+	/* increase the size of the buffers */
+	inbuflen += 512;
+	tmpbuflen += 512;
+	
+	inbuf = (char *)realloc(inbuf,(size_t)inbuflen);
+	if (!inbuf)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  inputerror = 1;
+	  break;
+	}
+	
+	tmpbuf = (char *)realloc(tmpbuf,(size_t)tmpbuflen);
+	if (!tmpbuf)
+	{
+	  log(LOG_ERR,"Could not allocate memory.");
+	  free(inbuf);
+	  inputerror = 1;
+	  break;
+	}
+      }
+      
+      retval=read_tout(s,inbuf+inputlen,(size_t)(inbuflen-inputlen-1),0, conn_toutp); /* subtract 1 from inbuflen to leave space for \0 */
+      if (retval <= 0)
+      {
+	log(LOG_ERR,"Error occured while receiving data.");
+	inputerror = 1;
+	break;
+      }
+      else
+      {
+	inputerror = 0;
+	inputlen += retval;
+	
+	/* exit clause if we have received CRLF or SMTPAP_ENDDATA, otherwise we need to keep reading*/
+	if ( (state == 6) && (inputlen >= SMTPAP_ENDDATA_LEN) )
+	{
+	  if (!strncmp(inbuf+inputlen-SMTPAP_ENDDATA_LEN,SMTPAP_ENDDATA,SMTPAP_ENDDATA_LEN))
+	    break;
+	}
+	else if ( (state != 6) && (inputlen >= SMTPAP_CRLF_LEN) )
+	{
+	  if (!strncmp(inbuf+inputlen-SMTPAP_CRLF_LEN,SMTPAP_CRLF,SMTPAP_CRLF_LEN))
+	    break;
+	}
+      }
+    } while(1); 
+    
+    if (inputerror != 0)
+      break;
+    
+    if (*inbuf == EOF)
+    {
+      log(LOG_DEBUG,"handle_connection() : Received EOF. Exiting.");
+      break;
+    }
+    
+    inbuf[inputlen]=0; /* add the terminating NULL character */
+    log(LOG_DEBUG, "Received this from client : %s",inbuf);
+    
+    /* save a copy of inbuf into tmpbuf, because calls to strtok() will change it */
+    strcpy(tmpbuf,inbuf);
+    
+    /* now handle input depending on the state */
+    
+    /* first check for a quit */
+    token = stolower((char *)strtok(inbuf,SMTPAP_SEPCHARS));
+    log(LOG_DEBUG,"handle_connection() : First token is %s.",token);
+    if ((!strcmp(token,SMTPAP_QUIT)) && (state != 6)) /* QUIT command - but doesn't count in state 6
+									* That's when we are receiving DATA input
+									*/
+    {
+      sendmessage(s,(char *)message, (size_t)512,"221 %s closing connection. Goodbye.%s",local->h_name,SMTPAP_CRLF);
+      break;
+    }
+    /* check for a RSET */
+    if ((!strcmp(token,SMTPAP_RSET)) && (state !=6)) /* RSET command - again, doesn't count in state 6 */
+    {
+      sendmessage(s,(char *)message,(size_t)512,"250 RSET received.%s",SMTPAP_CRLF);
+      /* clean up message state */
+      if (mailbuf != NULL)
+      {
+	free(mailbuf);
+	mailbuf = NULL;
+      }
+      if (rcptarray != NULL)
+      {
+	free(rcptarray);
+	rcptarray = NULL;
+      }
+      if (rcptbuf != NULL)
+      {
+	free(rcptbuf);
+	rcptbuf=NULL;
+      }
+      
+      close(sop);
+      
+      /* set state to 2/3 (depending on wether we have recieved HELO yet) and loop back and start again */
+      if (state != 2)
+	state=3;
+
+      continue;
+    }
+    
+    if (state == 1)
+    {
+      sendmessage(s,(char *)message, (size_t)512,"503 Connection refused. Please QUIT.%s",SMTPAP_CRLF);
+    }
+    else if (state == 2)
+    {
+      if ((!strcmp(token,SMTPAP_HELO)) || (!strcmp(token,SMTPAP_EHLO)))
+      {
+	token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	if (!token) /* no more tokens in inbuf */
+	{
+	  log(LOG_DEBUG,"handle_connection() : handle_connection : Received HELO/EHLO without arguments.");
+	  sendmessage(s,(char *)message,(size_t)512,"500 HELO/EHLO requires domain address.%s",SMTPAP_CRLF);
+	}
+	else
+	{
+	  log(LOG_DEBUG,"handle_connection() : handle_connection : Received HELO/EHLO with the following domain address : %s.",token);
+	  state =3;
+	  sendmessage(s,(char *)message,(size_t)512,"250 Hello user at %s. Pleased to meet you.%s",inet_ntoa(remote.sin_addr),SMTPAP_CRLF);
+	}
+      }
+      else
+	sendmessage(s,(char *)message,(size_t)512,"503 Expecting either HELO/EHLO or QUIT.%s",SMTPAP_CRLF);
+    }
+    else if (state == 3)
+    {
+      int further_check=0;
+      if ((!strncmp(token,SMTPAP_MAIL,SMTPAP_MAIL_LEN)))
+      {
+	token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	if (!token)
+	{
+	  sendmessage(s,(char *)message,(size_t)512,"500 MAIL requires From:<sender@address> .%s",SMTPAP_CRLF);
+	}
+	else
+	{
+	  stolower(token);
+	  if (!strcmp(token,"from:")) /* from: separate from the address */
+	  {
+	    token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	    if (token == NULL) /* expected another parameter but it's not there */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : Received MAIL From: without an address.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 MAIL From: requires sender address.%s",SMTPAP_CRLF);
+	      further_check = 0;
+	    }
+	    else /* continue further checking */
+	      further_check = 1;
+	  }
+	  else if (!strcmp(token,"from")) /* probably from : address */
+	  {
+	    token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	    if (token == NULL) /* not enough parameters */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : Received Mail From with no other parameters.");
+	      sendmessage(s,(char *)message,(size_t)512, "500 MAIL From: requires sender address.%s",SMTPAP_CRLF);
+	      further_check=0;
+	    }
+	    else if ( (token[0] == ':') && (token[1]!='\0') ) /* contains :address */
+	    {
+	      token++;
+	      further_check=1;
+	    }
+	    else if ( (token[0] == ':') && (token[1]=='\0') )/* the address is in the next token */
+	    {
+	      token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	      if (token == NULL) /* not enough parameters */
+	      {
+		log(LOG_DEBUG,"handle_connection() : Received Mail From : with no other parameters.");
+		sendmessage(s,(char *)message,(size_t)512,"500 MAIL From: requires sender address.%s",SMTPAP_CRLF);
+		further_check = 0;
+	      }
+	      else /* continue further checking */
+		further_check =1;
+	    }
+	    else /* couldn't find a colon (:) */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : Couldn't find a colon in the received MAIL command.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 There is a colon (:) missing in that command.%s",SMTPAP_CRLF);
+	      further_check = 1;
+	    }
+	  }
+	  else /* probably from:address */
+	  {
+	    if (!strncmp(token,"from:",5)) /* string starts with from: */
+	    {
+	      token += 5; /* skip the from: bit */
+	      further_check = 1; /* continue further checking */
+	    }
+	    else /* error */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : MAIL parameters don't start with from: .");
+	      sendmessage(s,(char *)message,(size_t)512,"500 MAIL requires From:<sender@address>.%s",SMTPAP_CRLF);
+	      further_check=0;
+	    }
+	  }
+	  if (further_check == 1) /* check that this is in the correct, format - we can't handle anything else
+				  * but straightforward <user@host> representation, <> optional
+				  */
+	  {
+	    if (((cp = (char *)strchr(token,',')) != NULL) || ((cp = (char *)strchr(token,':')) != NULL)) /* path contains , or : - can't cope with that */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : The client is probably trying to specify a reverse path, which I can't handle.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 I can only handle a simple return address.%s",SMTPAP_CRLF);
+	    }
+	    else if ((cp = (char *)strchr(token,'@')) == NULL) /* no @, that is most likely a problem :-) */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : The client specified a sender address with no @.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 Domain name required.%s",SMTPAP_CRLF);
+	    }
+	    else /* the mail command seems to be OK, save it */
+	    {
+	      if (mailbuf != NULL)
+		free(mailbuf);
+	      mailbuflen = strlen(tmpbuf) + 1;
+	      mailbuf = (char *)malloc(mailbuflen);
+	      if (!mailbuf)
+	      {
+		log(LOG_ERR,"Could not allocate memory.");
+		sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF);
+	      }
+	      else
+	      {
+		strncpy(mailbuf,tmpbuf,mailbuflen);
+		mailbuf[mailbuflen-1] = '\0'; /* add the terminating NULL character */
+		log(LOG_DEBUG,"handle_connection() : MAIL command saved as %s.",mailbuf);
+		
+		/* send an OK response to the client */
+		sendmessage(s,(char *)message,(size_t)512,"250 Sender address OK.%s",SMTPAP_CRLF);
+		state=4;
+	      }
+	    }
+	  }
+	}
+      }
+      else
+	sendmessage(s,(char *)message, (size_t)512,"503 Need MAIL first.%s",SMTPAP_CRLF);
+    }
+    else if(state == 4)
+    {
+      int further_check=0;
+      if ((!strcmp(token,SMTPAP_RCPT)))
+      {
+	token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	if (!token)
+	{
+	  sendmessage(s,(char *)message,(size_t)512,"500 RCPT requires To:<recipient@address> .%s",SMTPAP_CRLF);
+	}
+	else
+	{
+	  stolower(token);
+	  if (!strcmp(token,"to:")) /* to: separate from the address */
+	  {
+	    token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	    if (token == NULL) /* expected another parameter but it's not there */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : Received RCPT To: without an address.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 RCPT To: requires recipient address.%s",SMTPAP_CRLF);
+	      further_check = 0;
+	    }
+	    else /* continue further checking */
+	      further_check = 1;
+	  }
+	  else if (!strcmp(token,"to")) /* probably to : address or to :address */
+	  {
+	    token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	    if (token == NULL) /* not enough parameters */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : Received RCPT To with no other parameters.");
+	      sendmessage(s,(char *)message,(size_t)512, "500 RCPT To: requires recipient address.%s",SMTPAP_CRLF);
+	      further_check=0;
+	    }
+	    else if ( (token[0] == ':') && (token[1]!='\0') ) /* contains :address */
+	    {
+	      token++;
+	      further_check=1;
+	    }
+	    else if ( (token[0] == ':') && (token[1]=='\0') )/* the address is in the next token */
+	    {
+	      token = (char *)strtok(NULL,SMTPAP_SEPCHARS);
+	      if (token == NULL) /* not enough parameters */
+	      {
+		log(LOG_DEBUG,"handle_connection() : Received RCPT To : with no other parameters.");
+		sendmessage(s,(char *)message,(size_t)512,"500 RCPT To: requires recipient address.%s",SMTPAP_CRLF);
+		further_check = 0;
+	      }
+	      else /* continue further checking */
+		further_check =1;
+	    }
+	    else /* couldn't find a colon (:) */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : Couldn't find a colon in the received RCPT command.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 There is a colon (:) missing in that command.%s",SMTPAP_CRLF);
+	      further_check = 1;
+	    }
+	  }
+	  else /* probably to:address */
+	  {
+	    if (!strncmp(token,"to:",3)) /* string starts with from: */
+	    {
+	      token += 3; /* skip the to: bit */
+	      further_check = 1; /* continue further checking */
+	    }
+	    else /* error */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : RCPT parameters don't start with to: .");
+	      sendmessage(s,(char *)message,(size_t)512,"500 RCPT requires To:<recipient@address>.%s",SMTPAP_CRLF);
+	      further_check=0;
+	    }
+	  }
+	  if (further_check == 1) /* check that this is in the correct, format - we can't handle anything else
+				  * but straightforward <user@host> representation, <> optional
+				  */
+	  {
+	    if (((cp = (char *)strchr(token,',')) != NULL) || ((cp = (char *)strchr(token,':')) != NULL)) /* path contains , or : - can't cope with that */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : The client is probably trying to specify a forward path, which I can't handle.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 I can only handle a simple recipient address.%s",SMTPAP_CRLF);
+	    }
+	    else if ((cp = (char *)strchr(token,'@')) == NULL) /* no @, that is most likely a problem :-) */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : The client specified a recipient address with no @.");
+	      sendmessage(s,(char *)message,(size_t)512,"500 Domain name required.%s",SMTPAP_CRLF);
+	    }
+	    else /* the rcpt command seems to be OK, save it */
+	    {
+	      if (rcptbuf != NULL)
+	      {
+		free(rcptbuf);
+		rcptbuf = NULL;
+	      }
+	      rcptbuflen = strlen(tmpbuf) + 1;
+	      rcptbuf = (char *)malloc(rcptbuflen);
+	      if (!rcptbuf)
+	      {
+		log(LOG_ERR,"Could not allocate memory.");
+		sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF);
+	      }
+	      else
+	      {
+		strncpy(rcptbuf,tmpbuf,rcptbuflen);
+		rcptbuf[rcptbuflen-1] = '\0'; /* add the terminating NULL character */
+		log(LOG_DEBUG,"handle_connection() : handle_connection : RCPT command saved.");
+		
+		/* attempt to connect to the destination SMTP server through the OR network */
+		/* first extract the destination address */
+		dest_addr_str = extract_smtp_dest(rcptbuf);
+		log(LOG_DEBUG,"handle_connection() : handle_connection : called extract_smtp_dest()");
+		if (!dest_addr_str)
+		{
+		  log(LOG_DEBUG,"handle_connection() : Could not extract a destination SMTP address from the specified recipient address.");
+		  sendmessage(s,(char *)message,(size_t)512,"550 Could not extract destination domain.%s",SMTPAP_CRLF);
+		}
+		else
+		{
+		  /* fill in the standard structure */
+		  ss.version = VERSION;
+		  ss.protocol= SS_PROTOCOL_SMTP;
+		  ss.retry_count = 0;
+		  ss.addr_fmt = SS_ADDR_FMT_ASCII_HOST_PORT;
+		  
+		  /* open a socket for connecting to the proxy */
+		  sop = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+		  if (sop < 0)
+		  {
+		    log(LOG_DEBUG,"handle_connection() : handle_connection : Error opening socket.");
+		    sendmessage(s,(char *)message,(size_t)512,"451 Could not connect to the onion proxy.%s",SMTPAP_CRLF);
+		    if (dest_addr_str != NULL) {
+		      free(dest_addr_str);
+		      dest_addr_str = NULL;
+		    }
+		  }
+		  else
+		  {
+		    log(LOG_DEBUG,"handle_connection() : handle_connection : Socket opened.");
+		    memset((void *)&op_addr,0,sizeof(op_addr)); /* clear the structure first */
+		    /* set up the sockaddr_in structure */
+		    op_addr.sin_family=AF_INET;
+		    op_addr.sin_port=htons(op_port);
+		    memcpy((void *)&op_addr.sin_addr,local->h_addr,local->h_length);
+		    log(LOG_DEBUG,"handle_connection() : Trying to connect to %s at port %u.",inet_ntoa(*((struct in_addr *)local->h_addr)),op_port);
+		    
+		    /* try to connect */
+		    retval = connect(sop,(struct sockaddr *)&op_addr,sizeof(op_addr));
+		    if (retval == -1)
+		    {
+		      sendmessage(s,(char *)message,(size_t)512,"451 Could not connect to the onion proxy.%s",SMTPAP_CRLF);
+		      close(sop);
+		      if (dest_addr_str != NULL)
+		      {
+			free(dest_addr_str);
+			dest_addr_str = NULL;
+		      }
+		    }
+		    else /* connection established, now send the standard structure + address and wait for a response */
+		    {
+		      /* write the message to the op_out buffer */
+		      snprintf(dest_port_str,6,"%u",htons(SMTPAP_DEFAULT_SMTP_PORT));
+
+		      if (op_out != NULL)
+		      {
+			free(op_out);
+			op_out = NULL;
+		      }
+
+		      op_outlen = sizeof(ss) /* standard structure */
+			+ strlen(dest_addr_str) /* destination address */
+			  + 1 /* terminating NULL character */
+			+ strlen(dest_port_str)
+			  + 1; /* terminating NULL character */
+		      op_out = (char *)malloc(op_outlen);
+
+		      if (!op_out) /* error */
+		      {
+			log(LOG_DEBUG,"handle_connection() : handle_connection : Could not allocate memory.");
+			sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF);
+			close(sop);
+			if (dest_addr_str != NULL)
+			{
+			  free(dest_addr_str);
+			  dest_addr_str = NULL;
+			}
+		      }
+		      else
+		      {
+			memcpy(op_out,(void *)&ss,sizeof(ss));
+			strcpy(op_out+sizeof(ss), dest_addr_str);
+			strcpy(op_out+sizeof(ss)+strlen(dest_addr_str)+1,dest_port_str);
+			/* now send the message */
+			retval = write_tout(sop,op_out,op_outlen,conn_toutp);
+			/* now clean up the buffers */
+			op_outlen = 0;
+			free(op_out);
+			free(dest_addr_str);
+			if (retval == -1) /* send failed */
+			{
+			  log(LOG_DEBUG,"handle_connection() : handle_connection : send() failed.");
+			  sendmessage(s,(char *)message,(size_t)512,"451 Could not send to onion proxy.%s",SMTPAP_CRLF);
+			  close(sop);
+			}
+			else /* send seemed to have succeeded */
+			{
+			  /* wait for the return code */
+			  op_inlen = 1;
+			  op_in = (char *)malloc(op_inlen);
+			  if (!op_in) /* memory allocation failed */
+			  {
+			    log(LOG_DEBUG,"handle_connection() : handle_conection : Could not allocate memory.");
+			    sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF);
+			    close(sop);
+			  }
+			  else
+			  {
+			    retval = read_tout(sop,op_in,1,0, conn_toutp);
+			    if (retval <= 0) /* recv() failed */
+			    {
+			      log(LOG_DEBUG,"handle_connection() : handle_connection : recv() failed.");
+			      sendmessage(s,(char *)message,(size_t)512,"451 Could not receive data from the onion proxy.%s",SMTPAP_CRLF);
+			      close(sop);
+			    }
+			    else
+			    {
+			      if (!(*op_in)) /* onion proxy says OK */
+			      {
+				log(LOG_DEBUG,"handle_connection() : handle_connection : received E_SUCCESS from onion proxy");
+				/* clean up */
+				free(op_in);
+				op_inlen=0;
+				
+				/* allocate both op_in and op_out 512 bytes, the maximum size of an SMTP line */
+				op_outlen=512;
+				op_inlen=512;
+				op_out = (char *)malloc(512);
+				op_in = (char *)malloc(512);
+				if ((!op_out) || (!op_in))
+				{
+				  log(LOG_DEBUG,"handle_connection() : handle_connection : Could not allocate memory.");
+				  sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF);
+				  close(sop);
+				}
+				else
+				{
+				  /* receive the greeting message from the recipient */
+				  retval = receive(sop,&op_in,(size_t *)&op_inlen,0);
+				  if (retval == -1) /* could not receive greeting */
+				  {
+				    
+				    log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving greeting from recipient.");
+				    sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF);
+				  }
+				  else /* received greeting */
+				  {
+				    /* send HELO command */
+				    retval = sendmessage(sop,(char *)op_out,(size_t)op_outlen,"HELO ANONYMOUS.smtp.daemon%s",SMTPAP_CRLF);
+				    if (retval == -1)
+				    {
+				      sendmessage(s,(char *)message,(size_t)512,"451 Error sending HELO to the recipient.");
+				      close(sop);
+				    }
+				    else
+				    {
+				      
+				      retval = receive(sop,&op_in,(size_t *)&op_inlen,0);
+				      if (retval == -1)
+				      {
+					log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving HELO response from recipient");
+					sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF);
+					close(sop);
+				      }
+				      else
+				      {
+					op_in[retval]=0;
+					log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in);
+					if (op_in[0] == '2') /* success */
+					{
+					  /* send MAIL */
+					  if (options[Anonimize].r.i)
+					    retval = sendmessage(sop,(char *)op_out,(size_t)op_outlen,"MAIL From:anonymous@anon.net%s",SMTPAP_CRLF);
+					  else
+					    retval = write_tout(sop,mailbuf,mailbuflen-1,conn_toutp);
+					  if (retval == -1)
+					  {
+					    log(LOG_DEBUG,"handle_connection() : handle_connection : error sending MAIL to recipient");
+					    sendmessage(s,(char *)message,(size_t)512,"451 Error sending MAIL to the recipient.%s",SMTPAP_CRLF);
+					    sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF);
+					    close(sop);
+					  }
+					  else
+					  {
+					    retval = receive(sop,&op_in,(size_t *)&op_inlen,0);
+					    if (retval == -1)
+					    {
+					      log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving MAIL response from recipient");
+					      sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF);
+					      close(sop);
+					    }
+					    else
+					    {
+					      op_in[retval]=0;
+					      log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in);
+					      if (op_in[0] == '2') /* success */
+					      {
+						/* send RCPT */
+						retval = write_tout(sop,rcptbuf,rcptbuflen-1,conn_toutp); /* rcptbuflen includes the terminating NULL character but we don't want to send that */
+						if (retval == -1)
+						{
+						  log(LOG_DEBUG,"handle_connection() : handle_connection : error sending RCPT to recipient");
+						  sendmessage(s,(char *)message,(size_t)512,"451 Error sending RCPT to the recipient.%s",SMTPAP_CRLF);
+						  sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF);
+						  close(sop);
+						}
+						else
+						{
+						  retval = receive(sop,&op_in,(size_t *)&op_inlen,0);
+						  if (retval == -1)
+						  {
+						    log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving RCPT response from recipient");
+						    sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF);
+						    close(sop);
+						  }
+						  else
+						  {
+						    op_in[retval]=0;
+						    log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in);
+						    if (op_in[0] == '2') /* success */
+						    {
+						      sendmessage(s,(char *)message,(size_t)512,"250 Recipient OK.%s",SMTPAP_CRLF);
+						      state = 5;
+						    }
+						    else /* didn't like my RCPT */
+						    {
+						      log(LOG_DEBUG,"handle_connection() : handle_connection : RCPT unsuccessful");
+						      sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF);
+						      close(sop);
+						      sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my RCPT.%s",SMTPAP_CRLF);
+						    }
+						  }
+						}
+					      }
+					      else /* didn't like my MAIL */
+					      {
+						log(LOG_DEBUG,"handle_connection() : handle_connection : MAIL unsuccessful");
+						sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF);
+						close(sop);
+						sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my MAIL.%s",SMTPAP_CRLF);
+					      }
+					    }
+					  }
+					}
+					else
+					{
+					  log(LOG_DEBUG,"handle_connection() : handle_connection : HELO unsuccessful");
+					  sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF);
+					  close(sop);
+					  sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my HELO.%s",SMTPAP_CRLF);
+					}
+				      }
+				    }
+				  }
+				}
+			      }
+			      else
+			      {
+				log(LOG_DEBUG,"handle_connection() : handle_connection : onion proxy returned non-zero error code %d.",*op_in);
+				close(sop);
+				switch(*op_in)
+				{
+				 case SS_ERROR_VERSION_UNSUPPORTED :
+				  sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Protocol version not supported).%s",SMTPAP_CRLF);
+				  break;
+				 case SS_ERROR_ADDR_FMT_UNSUPPORTED:
+				  sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Address format not recognised).%s",SMTPAP_CRLF);
+				  break;
+				 case SS_ERROR_INVALID_ADDRESS:
+				  sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Invalid destination address).%s",SMTPAP_CRLF);
+				  break;
+				 case SS_ERROR_INVALID_PORT:
+				  sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Invalid destination port).%s",SMTPAP_CRLF);
+				  break;
+				 default :
+				  sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned unexpected error code %d.%s",*op_in,SMTPAP_CRLF);
+				  break;
+				}
+				/* clean up */
+				free(op_in);
+				op_inlen=0;
+			      }
+			    }
+			  }
+			}
+		      }
+		    }
+		  }
+		}
+	      }
+	    }
+	  }
+	}
+      }
+      else
+	sendmessage(s,(char *)message, (size_t)512,"503 Need RCPT first.%s",SMTPAP_CRLF);
+    }
+    else if (state == 5)
+    {
+      if ((!strcmp(token,SMTPAP_DATA))) /* received data */
+      {
+	partial_dataend = 0;
+	retval = write_tout(sop, tmpbuf, strlen(tmpbuf), conn_toutp); /* send DATA */
+	if (retval == -1) /* send(0) failed */
+	{
+	  log(LOG_DEBUG,"handle_connection() : handle_connection : Failed to send DATA to recipient.");
+	  sendmessage(s,(char *)message,(size_t)512,"451 Error sending DATA to the recipient.%s",SMTPAP_CRLF);
+	}
+	else /* get response from the recipient */
+	{
+	  retval = receive(sop,&op_in,(size_t *)&op_inlen,0);
+	  if (retval == -1)
+	  {
+	    log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving DATA response from recipient");
+	    sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF);
+	  }
+	  else
+	  {
+	    op_in[retval]=0;
+	    log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in);
+	    if (op_in[0] == '3') /* success */
+	    {
+	      sendmessage(s,(char *)message,(size_t)512,"354 Enter mail, end with \".\" on a line by itself%s",SMTPAP_CRLF);
+	      state = 6;
+	    }
+	    else /* didn't like my DATA */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : handle_connection : DATA unsuccessful");
+	      sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my DATA.%s",SMTPAP_CRLF);
+	    }
+	  }	  
+	}
+      }
+      else
+	sendmessage(s,(char *)message, (size_t)512,"503 Expecting DATA.%s",SMTPAP_CRLF);
+    }
+    else if (state == 6)
+    {
+      /* sanitize the data stream if necessary */
+      if (options[Anonimize].r.i == 1)
+      {
+	log(LOG_DEBUG,"handle_connection() : Sanitizing headers ...");
+	retval = sanitize_data((unsigned char **)&tmpbuf, &inputlen);
+      }
+      
+      if ((!retval) || (!options[Anonimize].r.i)) /* sanitization successsful (or wasn't necessary)? */
+      {
+	log(LOG_DEBUG,"handle_connection() : Attempting to send ...");
+	/* forward to recipient */
+	retval = write_tout(sop,tmpbuf, inputlen, conn_toutp);
+	if (retval == -1)
+	{
+	  log(LOG_DEBUG,"handle_connection() : handle_connection : Failed to forward data to recipient.");
+	  sendmessage(sop, (char *)op_out, (size_t)op_outlen,"451 Failed to forward data to the recipient.%s",SMTPAP_CRLF);
+	}
+	else
+	{
+	  /* get the response */
+	  retval = receive(sop,&op_in,(size_t *)&op_inlen,0);
+	  if (retval == -1)
+	  {
+	    log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving response from recipient");
+	    sendmessage(s,(char *)message,(size_t)512,"451 Data sent but did not receive a response from the recipient%s.",SMTPAP_CRLF);
+	  }
+	  else
+	  {
+	    op_in[retval]=0;
+	    log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in);
+	    if (op_in[0] == '2') /* success */
+	    {
+	      sendmessage(s,(char *)message,(size_t)512,"250 Message accepted for delivery.%s",SMTPAP_CRLF);
+	      sendmessage(sop, (char *)op_out, (size_t)op_outlen,"QUIT%s",SMTPAP_CRLF);
+	    }
+	    else /* didn't like my DATA */
+	    {
+	      log(LOG_DEBUG,"handle_connection() : handle_connection : DATA unsuccessful");
+	      sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my DATA.%s",SMTPAP_CRLF);
+	    }
+	  }
+	}
+      }
+      else /* sanitization error */
+      {
+	log(LOG_ERR,"Unable to sanitize an incoming message. Will reject.");
+	sendmessage(sop,(char *)op_out, (size_t)op_outlen,"400 Failed to sanitize the data stream.%s", SMTPAP_CRLF);
+      }
+      
+      /* after state 6 we go back to state 3, regardless of wether the transfer was succesful or not */
+      state = 3;
+      close(sop);
+      free(op_in);op_in=NULL;
+      free(op_out);op_out=NULL;
+    }
+    else /* unexpected state */
+    {
+      log(LOG_DEBUG,"handle_connection() : handle_connection : Unexpected state!");
+      log(LOG_ERR,"An unexpected error has occured. Closing connection.");
+      sendmessage(s,(char *)message,(size_t)512,"500 An unexpected error has ocurred. Closing connection.%s",SMTPAP_CRLF);
+      break;
+    }
+  }
+
+  /* clean up */
+  if (inbuf != NULL)
+    free(inbuf);
+  if (tmpbuf != NULL)
+    free(tmpbuf);
+  if (mailbuf != NULL)
+    free(mailbuf);
+  if (rcptbuf != NULL)
+    free(rcptbuf);
+  if (rcptarray != NULL)
+    free(rcptarray);
+  if (dest_addr_str != NULL)
+    free(dest_addr_str);
+  if (op_in != NULL)
+    free(op_in);
+  if (op_out != NULL)
+    free(op_out);
+  close(sop);
+  close(s);
+  
+  return retval;
+}
+
+int main(int argc, char *argv[])
+{
+  int retval = 0;
+  
+  char c; /* command-line option */
+  
+  /* configuration file */
+  char *conf_filename = NULL;
+  FILE *cf = NULL;
+  
+  struct hostent *local_host;
+  char local_hostname[512];
+  
+  struct sockaddr_in local, remote; /* local and remote address info */
+  
+  int request_sock; /* where we listen for connections */
+  int new_sock; /* for accepted connections */
+  
+  size_t sin_size; /* for accept() calls */
+  
+  u_short p; /* smtp proxy port */
+  u_short op_port; /* onion proxy port */
+  
+  /* used for reaping zombie processes */
+  struct sigaction sa;
+  
+  char *errtest = NULL; /* for detecting strtoul() errors */
+  
+  /* set default listening port */
+  p = htons(SMTPAP_LISTEN_PORT);
+  
+  /* deal with program arguments */
+  if ((argc < 2) && (argc > 5)) /* to few or too many arguments*/
+  {
+    print_usage();
+    return -1;
+  }
+  
+  opterr = 0;
+  while ((c = getopt(argc,argv,args)) != -1)
+  {
+    switch(c)
+    {
+     case 'f': /* config file */
+      conf_filename = optarg;
+      break;
+     case 'p':
+      p = htons((u_short)strtoul(optarg,&errtest,0));
+      if (errtest == optarg) /* error */
+      {
+	log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value.");
+	print_usage();
+	return -1;
+      }
+      break;
+     case 'h':
+      print_usage();
+      return 0;
+      break;
+     case 'l':
+      if (!strcmp(optarg,"emerg"))
+	loglevel = LOG_EMERG;
+      else if (!strcmp(optarg,"alert"))
+	loglevel = LOG_ALERT;
+      else if (!strcmp(optarg,"crit"))
+	loglevel = LOG_CRIT;
+      else if (!strcmp(optarg,"err"))
+	loglevel = LOG_ERR;
+      else if (!strcmp(optarg,"warning"))
+	loglevel = LOG_WARNING;
+      else if (!strcmp(optarg,"notice"))
+	loglevel = LOG_NOTICE;
+      else if (!strcmp(optarg,"info"))
+	loglevel = LOG_INFO;
+      else if (!strcmp(optarg,"debug"))
+	loglevel = LOG_DEBUG;
+      else
+      {
+	log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug.");
+	print_usage();
+	return -1;
+      }
+      break;
+     case '?':
+      if (isprint(c))
+	log(LOG_ERR,"Missing argument or unknown option '-%c'.",optopt);
+      else
+	log(LOG_ERR,"Unknown option character 'x%x'.",optopt);
+      print_usage();
+      return -1;
+      break;
+     default:
+      abort();
+    }
+  }
+  
+  /* the -f option is mandatory */
+  if (conf_filename == NULL)
+  {
+    log(LOG_ERR,"You must specify a config file with the -f option. See help (-h).");
+    return -1;
+  }
+  
+  /* load config file */
+  cf = open_config(conf_filename);
+  if (!cf)
+  {
+    log(LOG_ERR,"Could not open configuration file %s.",conf_filename);
+    return -1;
+  }
+  retval = parse_config(cf,options);
+  if (retval)
+    return -1;
+
+  if (options[OnionProxy].err != 1)
+  {
+    log(LOG_ERR,"The OnionProxy option is mandatory.");
+    return -1;
+  }
+  
+  if (options[MaxConn].err != 1)
+  {
+    log(LOG_ERR,"The MaxConn option is mandatory.");
+    return -1;
+  }
+  
+  if (options[Anonimize].err != 1)
+  {
+    log(LOG_ERR,"The Anonimize option is mandatory.");
+    return -1;
+  }
+  else if ((options[Anonimize].r.i != 0) && (options[Anonimize].r.i != 1))
+  {
+    log(LOG_ERR,"The Anonimize option takes the values 1 or 0.");
+    return -1;
+  }
+  
+  if (options[ConnTimeout].err != 1)
+  {
+    conn_tout.tv_sec = SMTPAP_DEFAULT_CONN_TIMEOUT;
+  }
+  else
+  {
+    if (!options[ConnTimeout].r.i)
+      conn_toutp = NULL;
+    else
+      conn_tout.tv_sec = options[ConnTimeout].r.i;
+  }
+  conn_tout.tv_usec = 0;
+  
+  op_port = (u_short)options[OnionProxy].r.i;
+  
+  /* get local address so that we know where to get the onion proxy when we need it */
+  retval = gethostname(local_hostname, (size_t)512);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Error getting local hostname");
+    return -1;
+  }
+  local_host = gethostbyname(local_hostname);
+  if (!local_host)
+  {
+    log(LOG_ERR,"Error getting local address.");
+    return -1;
+  }
+  log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname);
+  
+  /* get the server up and running */
+  request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
+  if (request_sock < 0)
+  {
+    log(LOG_ERR,"Error opening socket.");
+    return -1;
+  }
+  log(LOG_DEBUG,"Socket opened.");
+  memset((void *)&local,0,sizeof(local)); /* clear the structure first */
+  /* set up the sockaddr_in structure */
+  local.sin_family=AF_INET;
+  local.sin_addr.s_addr = INADDR_ANY;
+  local.sin_port=p;
+  /* bind it to the socket */
+  retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local));
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Error binding socket to local port %d.",ntohs(p));
+    return retval;
+  }
+  log(LOG_DEBUG,"Socket bound to port %d.",ntohs(p));
+  /* listen for connections */
+  retval = listen(request_sock,SOMAXCONN);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Could not listen for connections.");
+    return retval;
+  }
+  log(LOG_DEBUG,"Listening for connections.");
+  /* server should now be up and running */
+
+  /* install the signal handler for making sure zombie processes are killed */
+  sa.sa_handler = sigchld_handler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_RESTART;
+  retval = sigaction(SIGCHLD,&sa,NULL);
+  if (retval < 0)
+  {
+    log(LOG_ERR,"Could not install a signal handler.");
+    return -1;
+  }
+
+  /* main server loop */
+  /* I use a forking server technique - this isn't the most efficient way to do it,
+   * but it is simpler. */
+  while(1) 
+  {
+    sin_size = sizeof(struct sockaddr_in);
+    new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size);
+    if (new_sock == -1)
+    {
+      if (errno != EINTR)
+	log(LOG_ERR,"Could not accept socket connection.");
+      else
+	log(LOG_DEBUG,"Interrupt received.");
+      continue;
+    }
+    if (connections >= options[MaxConn].r.i)
+    {
+      log(LOG_NOTICE,"Number of maximum connections reached. Rejecting incoming request.");
+      close(new_sock);
+      continue;
+    }
+    
+    log(LOG_DEBUG,"Accepted a connection from %s.",inet_ntoa(remote.sin_addr));
+    connections++;
+    
+    if (!fork()) /* this is the child process */
+    {
+      close(request_sock); /* the child doesn't need the request socket anymore */
+
+      /* Main logic of smtpap. */
+      retval = handle_connection(new_sock, local_host, remote, op_port);
+      /* End main logic */
+      
+      exit(retval); /* done, exit */
+    }
+    
+    close(new_sock); /* don't need this anymore */
+  }
+
+  return retval;
+
+}
+

+ 90 - 0
src/smtpap/smtpap.h

@@ -0,0 +1,90 @@
+/**
+ * smtpap.h
+ * SMTP Application Proxy for Onion Routing
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1  2002/06/26 22:45:50  arma
+ * Initial revision
+ *
+ * Revision 1.12  2002/01/29 01:00:10  mp292
+ * All network operations are now timeoutable.
+ *
+ * Revision 1.11  2002/01/26 21:50:17  mp292
+ * Reviewed according to Secure-Programs-HOWTO. Still need to deal with network
+ * timeouts.
+ *
+ * Revision 1.10  2001/12/18 14:56:29  badbytes
+ * *** empty log message ***
+ *
+ * Revision 1.9  2001/12/18 14:43:19  badbytes
+ * Added DEFAULT_SMTP_PORT.
+ *
+ * Revision 1.8  2001/12/12 16:02:29  badbytes
+ * Testing completed.
+ *
+ * Revision 1.7  2001/12/11 10:43:21  badbytes
+ * MAIL and RCPT handling completed. Still coding connection to Onion Proxy.
+ *
+ * Revision 1.6  2001/12/10 16:10:35  badbytes
+ * Wrote a tokenize() function to help with parsing input from SMTP clients.
+ *
+ * Revision 1.5  2001/12/07 15:02:43  badbytes
+ * Server setup code completed.
+ *
+ */
+
+#ifndef __SMTPAP_H
+
+#define __SMTPAP_H
+
+#define SMTPAP_CRLF "\015\012"
+#define SMTPAP_CRLF_LEN 2
+
+#define SMTPAP_CR '\015'
+#define SMTPAP_LF '\012'
+
+/* terminator for DATA input */
+#define SMTPAP_ENDDATA "\015\012.\015\012" 
+#define SMTPAP_ENDDATA_LEN 5
+
+/* characters that separate tokens in SMTPAP commands */
+#define SMTPAP_SEPCHARS " \t\015\012" /* for general commands */
+#define SMTPAP_PATH_SEPCHARS " \t\015\012<>" /* for forward and reverse path */
+
+/* default listening port */
+#define SMTPAP_LISTEN_PORT 25
+
+/* default SMTP port */
+#define SMTPAP_DEFAULT_SMTP_PORT 25
+
+/* default connection timeout */
+#define SMTPAP_DEFAULT_CONN_TIMEOUT 120; /* 120s */
+
+/* SMTP commands and their lengths */
+#define SMTPAP_QUIT "quit"
+#define SMTPAP_QUIT_LEN 4
+
+#define SMTPAP_HELO "helo"
+#define SMTPAP_HELO_LEN 4
+#define SMTPAP_EHLO "ehlo"
+#define SMTPAP_EHLO_LEN 4
+
+#define SMTPAP_MAIL "mail"
+#define SMTPAP_MAIL_LEN 4
+
+#define SMTPAP_RSET "rset"
+#define SMTPAP_RSET_LEN 4
+
+#define SMTPAP_RCPT "rcpt"
+#define SMTPAP_RCPT_LEN 4
+
+#define SMTPAP_DATA "data"
+#define SMTPAP_DATA_LEN 4
+
+#endif
+