aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Dingledine <arma@torproject.org>2002-06-26 22:45:49 +0000
committerRoger Dingledine <arma@torproject.org>2002-06-26 22:45:49 +0000
commit9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a (patch)
treefac560bf2dce8a8d2b82e296b71ff24f59ab1a7a
parent766a465a6043ac4e643c398feb14f708fd0d863f (diff)
downloadtor-9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a.tar
tor-9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a.tar.gz
Initial revision
svn:r2
-rw-r--r--TODO124
-rw-r--r--src/common/Makefile228
-rw-r--r--src/common/cell.c355
-rw-r--r--src/common/cell.h95
-rw-r--r--src/common/config.c347
-rw-r--r--src/common/config.h88
-rw-r--r--src/common/key.c89
-rw-r--r--src/common/key.h32
-rw-r--r--src/common/log.c74
-rw-r--r--src/common/log.h40
-rw-r--r--src/common/onion.c633
-rw-r--r--src/common/onion.h156
-rw-r--r--src/common/opcell.c77
-rw-r--r--src/common/opcell.h44
-rw-r--r--src/common/policies.h39
-rw-r--r--src/common/routent.c147
-rw-r--r--src/common/routent.h189
-rw-r--r--src/common/scheduler.c260
-rw-r--r--src/common/scheduler.h57
-rw-r--r--src/common/ss.h63
-rw-r--r--src/common/utils.c150
-rw-r--r--src/common/utils.h53
-rw-r--r--src/common/version.h28
-rw-r--r--src/httpap/Makefile99
-rw-r--r--src/httpap/http.c194
-rw-r--r--src/httpap/http.h46
-rw-r--r--src/httpap/httpap.c702
-rw-r--r--src/httpap/httpap.h33
-rw-r--r--src/op/Makefile285
-rw-r--r--src/op/args.c121
-rw-r--r--src/op/args.h41
-rw-r--r--src/op/auth.c85
-rw-r--r--src/op/auth.h23
-rw-r--r--src/op/buffers.c146
-rw-r--r--src/op/buffers.h25
-rw-r--r--src/op/config.c49
-rw-r--r--src/op/config.h30
-rw-r--r--src/op/crypto.c104
-rw-r--r--src/op/crypto.h23
-rw-r--r--src/op/op.c916
-rw-r--r--src/op/op.h54
-rw-r--r--src/op/routers.c364
-rw-r--r--src/op/routers.h66
-rw-r--r--src/op/ss.c194
-rw-r--r--src/op/ss.h22
-rw-r--r--src/or/Makefile18
-rw-r--r--src/or/args.c99
-rw-r--r--src/or/buffers.c174
-rw-r--r--src/or/cell.c23
-rw-r--r--src/or/circuit.c319
-rw-r--r--src/or/command.c212
-rw-r--r--src/or/config.c49
-rw-r--r--src/or/connection.c362
-rw-r--r--src/or/connection_app.c212
-rw-r--r--src/or/connection_op.c130
-rw-r--r--src/or/connection_or.c580
-rw-r--r--src/or/main.c406
-rw-r--r--src/or/onion.c62
-rw-r--r--src/or/or.h411
-rw-r--r--src/or/routers.c365
-rw-r--r--src/orkeygen/Makefile58
-rw-r--r--src/orkeygen/orkeygen.c119
-rw-r--r--src/smtpap/Makefile80
-rw-r--r--src/smtpap/io.c133
-rw-r--r--src/smtpap/io.h2
-rw-r--r--src/smtpap/smtpap.c1393
-rw-r--r--src/smtpap/smtpap.h90
67 files changed, 12287 insertions, 0 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 000000000..e21f2134b
--- /dev/null
+++ b/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.
+
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 000000000..3b0e45159
--- /dev/null
+++ b/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
diff --git a/src/common/cell.c b/src/common/cell.c
new file mode 100644
index 000000000..e742a80f5
--- /dev/null
+++ b/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;
+}
diff --git a/src/common/cell.h b/src/common/cell.h
new file mode 100644
index 000000000..726c01a7f
--- /dev/null
+++ b/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
+
diff --git a/src/common/config.c b/src/common/config.c
new file mode 100644
index 000000000..99961f147
--- /dev/null
+++ b/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;
+}
diff --git a/src/common/config.h b/src/common/config.h
new file mode 100644
index 000000000..15d9b1e5d
--- /dev/null
+++ b/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
diff --git a/src/common/key.c b/src/common/key.c
new file mode 100644
index 000000000..ab91322d7
--- /dev/null
+++ b/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 */
+}
diff --git a/src/common/key.h b/src/common/key.h
new file mode 100644
index 000000000..ad41f1d88
--- /dev/null
+++ b/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
diff --git a/src/common/log.c b/src/common/log.c
new file mode 100644
index 000000000..e85f2c946
--- /dev/null
+++ b/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);
+}
diff --git a/src/common/log.h b/src/common/log.h
new file mode 100644
index 000000000..e70aa2f5b
--- /dev/null
+++ b/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
diff --git a/src/common/onion.c b/src/common/onion.c
new file mode 100644
index 000000000..2b42f3d95
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/common/onion.h b/src/common/onion.h
new file mode 100644
index 000000000..41e6d286c
--- /dev/null
+++ b/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
diff --git a/src/common/opcell.c b/src/common/opcell.c
new file mode 100644
index 000000000..288a355ce
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/common/opcell.h b/src/common/opcell.h
new file mode 100644
index 000000000..5eca59bd5
--- /dev/null
+++ b/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
+
diff --git a/src/common/policies.h b/src/common/policies.h
new file mode 100644
index 000000000..78ba20c50
--- /dev/null
+++ b/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 */
diff --git a/src/common/routent.c b/src/common/routent.c
new file mode 100644
index 000000000..0f5f3e7da
--- /dev/null
+++ b/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;
+}
diff --git a/src/common/routent.h b/src/common/routent.h
new file mode 100644
index 000000000..6a905a9bb
--- /dev/null
+++ b/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
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
new file mode 100644
index 000000000..3fb02315a
--- /dev/null
+++ b/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;
+}
diff --git a/src/common/scheduler.h b/src/common/scheduler.h
new file mode 100644
index 000000000..be4d72320
--- /dev/null
+++ b/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
diff --git a/src/common/ss.h b/src/common/ss.h
new file mode 100644
index 000000000..9298fa1b7
--- /dev/null
+++ b/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
diff --git a/src/common/utils.c b/src/common/utils.c
new file mode 100644
index 000000000..1a6fa6498
--- /dev/null
+++ b/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;
+ }
+}
+
diff --git a/src/common/utils.h b/src/common/utils.h
new file mode 100644
index 000000000..ea836de5c
--- /dev/null
+++ b/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
diff --git a/src/common/version.h b/src/common/version.h
new file mode 100644
index 000000000..fa9b87e34
--- /dev/null
+++ b/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
diff --git a/src/httpap/Makefile b/src/httpap/Makefile
new file mode 100644
index 000000000..bb16e1ad4
--- /dev/null
+++ b/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
diff --git a/src/httpap/http.c b/src/httpap/http.c
new file mode 100644
index 000000000..09116cec7
--- /dev/null
+++ b/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;
+}
diff --git a/src/httpap/http.h b/src/httpap/http.h
new file mode 100644
index 000000000..cc2d20ccf
--- /dev/null
+++ b/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);
diff --git a/src/httpap/httpap.c b/src/httpap/httpap.c
new file mode 100644
index 000000000..dbcb2e630
--- /dev/null
+++ b/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;
+
+}
+
diff --git a/src/httpap/httpap.h b/src/httpap/httpap.h
new file mode 100644
index 000000000..d15516141
--- /dev/null
+++ b/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
+
diff --git a/src/op/Makefile b/src/op/Makefile
new file mode 100644
index 000000000..d1732f99e
--- /dev/null
+++ b/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
diff --git a/src/op/args.c b/src/op/args.c
new file mode 100644
index 000000000..6e3a4600f
--- /dev/null
+++ b/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;
+}
diff --git a/src/op/args.h b/src/op/args.h
new file mode 100644
index 000000000..0d693efab
--- /dev/null
+++ b/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
diff --git a/src/op/auth.c b/src/op/auth.c
new file mode 100644
index 000000000..3a3f40ce8
--- /dev/null
+++ b/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;
+}
diff --git a/src/op/auth.h b/src/op/auth.h
new file mode 100644
index 000000000..97be419bf
--- /dev/null
+++ b/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);
diff --git a/src/op/buffers.c b/src/op/buffers.c
new file mode 100644
index 000000000..39a4e442d
--- /dev/null
+++ b/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;
+}
diff --git a/src/op/buffers.h b/src/op/buffers.h
new file mode 100644
index 000000000..a161e0810
--- /dev/null
+++ b/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);
diff --git a/src/op/config.c b/src/op/config.c
new file mode 100644
index 000000000..33e5a394a
--- /dev/null
+++ b/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;
+}
diff --git a/src/op/config.h b/src/op/config.h
new file mode 100644
index 000000000..190296e76
--- /dev/null
+++ b/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);
diff --git a/src/op/crypto.c b/src/op/crypto.c
new file mode 100644
index 000000000..effa768b8
--- /dev/null
+++ b/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;
+}
diff --git a/src/op/crypto.h b/src/op/crypto.h
new file mode 100644
index 000000000..17c61e669
--- /dev/null
+++ b/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);
diff --git a/src/op/op.c b/src/op/op.c
new file mode 100644
index 000000000..06ae99950
--- /dev/null
+++ b/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;
+
+}
+
diff --git a/src/op/op.h b/src/op/op.h
new file mode 100644
index 000000000..f45d387dd
--- /dev/null
+++ b/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
diff --git a/src/op/routers.c b/src/op/routers.c
new file mode 100644
index 000000000..943b08cf9
--- /dev/null
+++ b/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);
+}
diff --git a/src/op/routers.h b/src/op/routers.h
new file mode 100644
index 000000000..7cacb4daf
--- /dev/null
+++ b/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
diff --git a/src/op/ss.c b/src/op/ss.c
new file mode 100644
index 000000000..3437517e4
--- /dev/null
+++ b/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;
+}
diff --git a/src/op/ss.h b/src/op/ss.h
new file mode 100644
index 000000000..d6e68240f
--- /dev/null
+++ b/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);
diff --git a/src/or/Makefile b/src/or/Makefile
new file mode 100644
index 000000000..edf2398b4
--- /dev/null
+++ b/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}
+
diff --git a/src/or/args.c b/src/or/args.c
new file mode 100644
index 000000000..fc3837ca8
--- /dev/null
+++ b/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;
+}
diff --git a/src/or/buffers.c b/src/or/buffers.c
new file mode 100644
index 000000000..6900ca300
--- /dev/null
+++ b/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;
+
+}
+
diff --git a/src/or/cell.c b/src/or/cell.c
new file mode 100644
index 000000000..ef53a9eae
--- /dev/null
+++ b/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 */
+}
+
diff --git a/src/or/circuit.c b/src/or/circuit.c
new file mode 100644
index 000000000..b910fd351
--- /dev/null
+++ b/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);
+ }
+}
+
diff --git a/src/or/command.c b/src/or/command.c
new file mode 100644
index 000000000..2392a9bf2
--- /dev/null
+++ b/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);
+
+}
+
diff --git a/src/or/config.c b/src/or/config.c
new file mode 100644
index 000000000..535f7638d
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/or/connection.c b/src/or/connection.c
new file mode 100644
index 000000000..7411852ac
--- /dev/null
+++ b/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 */
+}
+
diff --git a/src/or/connection_app.c b/src/or/connection_app.c
new file mode 100644
index 000000000..94276fd59
--- /dev/null
+++ b/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;
+}
+
+
+
diff --git a/src/or/connection_op.c b/src/or/connection_op.c
new file mode 100644
index 000000000..03d36dbe6
--- /dev/null
+++ b/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);
+}
+
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
new file mode 100644
index 000000000..813cc8975
--- /dev/null
+++ b/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);
+}
+
diff --git a/src/or/main.c b/src/or/main.c
new file mode 100644
index 000000000..85f91ab30
--- /dev/null
+++ b/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;
+
+}
+
diff --git a/src/or/onion.c b/src/or/onion.c
new file mode 100644
index 000000000..d9c8a9c5d
--- /dev/null
+++ b/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;
+}
+
diff --git a/src/or/or.h b/src/or/or.h
new file mode 100644
index 000000000..1d73e123a
--- /dev/null
+++ b/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
diff --git a/src/or/routers.c b/src/or/routers.c
new file mode 100644
index 000000000..d24d4d1bd
--- /dev/null
+++ b/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);
+}
diff --git a/src/orkeygen/Makefile b/src/orkeygen/Makefile
new file mode 100644
index 000000000..20c65c457
--- /dev/null
+++ b/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
diff --git a/src/orkeygen/orkeygen.c b/src/orkeygen/orkeygen.c
new file mode 100644
index 000000000..718f888f4
--- /dev/null
+++ b/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);
+
+ }
diff --git a/src/smtpap/Makefile b/src/smtpap/Makefile
new file mode 100644
index 000000000..e10605e0a
--- /dev/null
+++ b/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
diff --git a/src/smtpap/io.c b/src/smtpap/io.c
new file mode 100644
index 000000000..57ea72854
--- /dev/null
+++ b/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;
+}
diff --git a/src/smtpap/io.h b/src/smtpap/io.h
new file mode 100644
index 000000000..d5515adb4
--- /dev/null
+++ b/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);
diff --git a/src/smtpap/smtpap.c b/src/smtpap/smtpap.c
new file mode 100644
index 000000000..701dde809
--- /dev/null
+++ b/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;
+
+}
+
diff --git a/src/smtpap/smtpap.h b/src/smtpap/smtpap.h
new file mode 100644
index 000000000..3e875f4d1
--- /dev/null
+++ b/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
+