summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2006-09-23 22:06:09 +0000
committerNick Mathewson <nickm@torproject.org>2006-09-23 22:06:09 +0000
commitbf738d30808190fcd20e8bcb3e75b17f7b730105 (patch)
tree4cfb162ee476acb4620d1d8195bd9ca850afe530
parentaef0c26cb125f69113fc8ae37d4c3a8c062525e9 (diff)
downloadtor-bf738d30808190fcd20e8bcb3e75b17f7b730105.tar.gz
tor-bf738d30808190fcd20e8bcb3e75b17f7b730105.zip
r6362@totoro (orig r6361): arma | 2006-04-10 05:43:30 -0400
another todo item we ought to do r6363@totoro (orig r6362): weasel | 2006-04-10 06:03:47 -0400 New upstream version r6364@totoro (orig r6363): phobos | 2006-04-10 10:12:29 -0400 Remove a dependency on dist and assume a tarball is in ".." for dist-rpm. r6365@totoro (orig r6364): weasel | 2006-04-10 15:39:26 -0400 Remove redundant includes. They are all hanled in torint.h which we already do include. r6370@totoro (orig r6369): arma | 2006-04-10 16:00:31 -0400 be willing to add our own routerinfo into the routerlist. this means authorities will include themselves in their directories and network-statuses. r6373@totoro (orig r6372): arma | 2006-04-10 16:08:12 -0400 and forward-port the man page change r6374@totoro (orig r6373): arma | 2006-04-10 16:16:46 -0400 make DirFetchPeriod and StatusFetchPeriod truly obsolete. r6376@totoro (orig r6375): arma | 2006-04-10 16:21:55 -0400 0.1.2.0-alpha-cvs! r6377@totoro (orig r6376): nickm | 2006-04-10 17:23:00 -0400 Remove DER64 functions in trunk: they will never be used again unless the directory authorities switch back to 0.0.9tooearly. r6378@totoro (orig r6377): weasel | 2006-04-10 17:29:29 -0400 Stop assuming every authority is a v1 authority r6381@totoro (orig r6380): weasel | 2006-04-10 17:37:20 -0400 [forward port] Update the list of documentation files in tor.nsi and package_nsis-weasel.sh r6382@totoro (orig r6381): arma | 2006-04-10 17:40:43 -0400 drop moria1 from the list of authorities. r6383@totoro (orig r6382): arma | 2006-04-11 10:12:04 -0400 ok, put moria1 back in, since it's v1 and thus we need to send our rendezvous descriptors to it. eventually we might make a 'v1only' tag that explains it's only for rendezvous descriptors. r6384@totoro (orig r6383): phobos | 2006-04-12 21:46:27 -0400 Temporary hacks to ensure make dist-rpm works from cvs until a better solution can be found. r6386@totoro (orig r6385): arma | 2006-04-14 16:19:33 -0400 forward-port the n_named log severity downgrade. r6388@totoro (orig r6387): arma | 2006-04-15 03:15:23 -0400 if the bottom eighth of the servers by bandwidth is really crummy, try the bottom quartile instead. r6389@totoro (orig r6388): arma | 2006-04-15 19:53:58 -0400 update spec to reflect the downgraded loglevel for naming complaints. r6390@totoro (orig r6389): arma | 2006-04-16 18:34:00 -0400 no need to escape the address for our connections -- they are always IP addresses. r6391@totoro (orig r6390): arma | 2006-04-16 18:44:08 -0400 better error checking for torify, contributed by jacob appelbaum. r6392@totoro (orig r6391): arma | 2006-04-16 18:48:41 -0400 and remove the 'debugging' flag for torify r6393@totoro (orig r6392): weasel | 2006-04-16 22:46:14 -0400 Add an XXX to torify.in - "which" is evil, quote a few variables do -h and --help add (c) and license ("Same as tor") print an error message if exec falls through r6394@totoro (orig r6393): arma | 2006-04-17 02:43:27 -0400 slightly clearer log message when you use a nickname rather than a key for an unnamed server. r6395@totoro (orig r6394): arma | 2006-04-17 02:46:20 -0400 note another bug: we complain that a router doesn't exist, because it's down so we never fetched it so it doesn't exist. r6397@totoro (orig r6396): nickm | 2006-04-17 23:07:24 -0400 forward-port: "Resolve" all XXX011 items, mostly by marking them non-011. r6399@totoro (orig r6398): nickm | 2006-04-17 23:36:28 -0400 [forward-port] Implement an option, VirtualAddrMask, to set which addresses get handed out in response to mapaddress requests. Needs testing and docs! r6401@totoro (orig r6400): nickm | 2006-04-17 23:51:18 -0400 [Forward-port ]Test and document last patch. r6402@totoro (orig r6401): nickm | 2006-04-17 23:58:42 -0400 mainline branch. Remove some more dead XXXs. r6403@totoro (orig r6402): nickm | 2006-04-18 00:57:07 -0400 require at least 2**16 assignable virtual addresses r6405@totoro (orig r6404): arma | 2006-04-18 01:03:09 -0400 class B really means /16 here r6407@totoro (orig r6406): arma | 2006-04-18 15:48:06 -0400 Raise the timeout for complaining about wedged cpuworkers. This value is high because some servers with low memory/cpu sometimes spend an hour or more swapping, and Tor starves. r6408@totoro (orig r6407): weasel | 2006-04-21 11:52:49 -0400 Add add-tor helper script to contrib r6409@totoro (orig r6408): weasel | 2006-04-21 11:57:09 -0400 Remove test values from add-tor r6410@totoro (orig r6409): nickm | 2006-04-23 17:36:52 -0400 Prioritize items for 0.1.2 r6411@totoro (orig r6410): nickm | 2006-04-23 17:40:15 -0400 And another 0.1.2 item r6412@totoro (orig r6411): arma | 2006-04-23 19:05:34 -0400 make more hibernate log messages use local time. we should audit to see what other log messages keep switching back and forth between GMT and local. r6413@totoro (orig r6412): arma | 2006-04-23 19:09:03 -0400 Regenerate our local descriptor if it's dirty and some local function asks for it. This may resolve bug 286. r6414@totoro (orig r6413): nickm | 2006-04-24 12:29:06 -0400 Add a stub of a "path-spec", containing only the helper-node emails from arma. whee. r6415@totoro (orig r6414): nickm | 2006-04-24 13:51:31 -0400 Possible partial fix for 285; needs review r6416@totoro (orig r6415): arma | 2006-04-24 17:11:56 -0400 resolve typos in add-tor contrib script r6417@totoro (orig r6416): nickm | 2006-04-25 01:42:09 -0400 Add some bullet points to write up r6418@totoro (orig r6417): nickm | 2006-04-25 01:59:31 -0400 fix a segfault in last bug-285-related commit. r6419@totoro (orig r6418): nickm | 2006-04-25 02:02:46 -0400 you wanted it spelled properly too? And me not even funded! r6420@totoro (orig r6419): arma | 2006-04-25 02:16:38 -0400 list some more items to remember in path-building r6421@totoro (orig r6420): nickm | 2006-04-25 02:20:47 -0400 Only warn about a down node once r6422@totoro (orig r6421): arma | 2006-04-25 03:00:04 -0400 petty cleanups r6423@totoro (orig r6422): arma | 2006-04-25 03:06:48 -0400 put one of the XXX's back in r6427@totoro (orig r6426): nickm | 2006-04-29 13:44:31 -0400 shorten some too-wide lines r6428@totoro (orig r6427): nickm | 2006-04-29 14:42:26 -0400 Start remembering *where* we are storing routerdescs. This will make us easier to move from a RAM-mirrors-disk model to a RAM-caches-disk model, and save maybe around 10MB on a directory server. r6429@totoro (orig r6428): nickm | 2006-04-29 14:43:05 -0400 Note some subtasks and difficulties involved with reducing RAM usage on dirservers r6431@totoro (orig r6430): arma | 2006-05-03 14:29:44 -0400 forward-port the ORPort==0 patch r6434@totoro (orig r6433): arma | 2006-05-03 14:32:15 -0400 forward-port the changelog r6438@totoro (orig r6437): phobos | 2006-05-03 20:34:51 -0400 Reworked dist-rpm in order to duplicate what dist used to do, but don't actually require dist. r6444@totoro (orig r6443): weasel | 2006-05-05 11:40:54 -0400 Merge differences between debian_version_0_1_1_18-rc-1 and debian_version_0_1_1_19-rc-1 from tor-0_1_1-patches into head r6445@totoro (orig r6444): weasel | 2006-05-05 12:58:38 -0400 Handle website/* in tor.nsi r6448@totoro (orig r6447): arma | 2006-05-08 00:28:49 -0400 stop telling people that "tor -h" will help them in any way. r6449@totoro (orig r6448): arma | 2006-05-09 05:47:47 -0400 Tor servers are also giving spurious "you're invalid" warnings. This is because we get a lot of network statuses that don't list us at all, and we conclude that they all think we're invalid. The long-term fix is to get better logic, and the short-term fix is to downgrade the log severity. r6451@totoro (orig r6450): arma | 2006-05-10 03:35:03 -0400 another piece of doing tor over udp that i am concerned about. r6452@totoro (orig r6451): arma | 2006-05-10 03:35:33 -0400 a few more tweaks to the faq. r6453@totoro (orig r6452): weasel | 2006-05-10 06:24:17 -0400 All these headers we get via torint.h r6455@totoro (orig r6454): phobos | 2006-05-16 01:48:08 -0400 Fixed //Library/Tor in Tor.loc for osx r6456@totoro (orig r6455): nickm | 2006-05-16 22:18:35 -0400 finally write some comments on tor-spec-udp.txt r6457@totoro (orig r6456): nickm | 2006-05-21 16:01:13 -0400 apply control-spec patch from Matt Edman: Circuit status only has a path when it has been extended one or more hops. r6458@totoro (orig r6457): arma | 2006-05-22 00:44:57 -0400 add a few items it would be smart todo r6459@totoro (orig r6458): arma | 2006-05-22 15:56:32 -0400 remove all the interim changelog stuff for 0.1.1.x r6460@totoro (orig r6459): arma | 2006-05-22 16:00:12 -0400 my current notes on a 0.1.1.20 changelog r6461@totoro (orig r6460): arma | 2006-05-22 16:16:18 -0400 other todo tweaks r6462@totoro (orig r6461): arma | 2006-05-22 16:26:30 -0400 shuffle the todo items some more r6464@totoro (orig r6463): nickm | 2006-05-22 19:24:06 -0400 Remove string size limit on NEWDESC messages; solve bug 291. r6466@totoro (orig r6465): arma | 2006-05-22 23:08:30 -0400 add a few more debugging lines to help mikec track down his 11 minute jump into the future. r6467@totoro (orig r6466): arma | 2006-05-22 23:27:39 -0400 two more todo items that need to be solved during the wsaenobufs quest. r6468@totoro (orig r6467): arma | 2006-05-23 00:05:45 -0400 claim a few of the todo items. i guess that means i'm hoping nick does the rest. ;) r6469@totoro (orig r6468): arma | 2006-05-23 02:20:35 -0400 continue messing with the changelog. it's getting better now. r6470@totoro (orig r6469): arma | 2006-05-23 03:03:30 -0400 document that runasdaemon has no effect on windows. r6472@totoro (orig r6471): arma | 2006-05-23 03:04:55 -0400 ignore RunAsDaemon more thoroughly when we're running on windows. r6474@totoro (orig r6473): nickm | 2006-05-23 04:23:03 -0400 Throw out this UNALIGNED_INT_ACCESS_OK nonsense. Even where it works, it is often way way slower than doing the right thing. Backport candidate. r6475@totoro (orig r6474): nickm | 2006-05-23 04:38:18 -0400 Patch from Michael Mohr to fix cross-compilation. Backport candidate. Tweaked to use sensible defaults for NULL_REP_IS_ZERO_BYTES and TIME_T_IS_SIGNED. r6476@totoro (orig r6475): nickm | 2006-05-23 04:50:39 -0400 Add cross.sh cross-compilation script from Michael Mohr. Trivial backport candidate, since adding a new script cannot possibly break anything. r6477@totoro (orig r6476): nickm | 2006-05-23 04:54:26 -0400 Mark cross-compilation as solved in TODO. r6478@totoro (orig r6477): arma | 2006-05-23 11:06:05 -0400 trim out the parts of cross.sh that don't make sense now that we've applied cross-path directly. also, now we can run cross.sh from the tarball, not just from cvs. r6479@totoro (orig r6478): arma | 2006-05-23 11:26:51 -0400 another todo item that will make goodell happy r6480@totoro (orig r6479): arma | 2006-05-23 13:00:49 -0400 final changelog for 0.1.1.20. it is done. r6487@totoro (orig r6486): weasel | 2006-05-23 14:19:36 -0400 Forward port changelog r6488@totoro (orig r6487): arma | 2006-05-23 16:15:51 -0400 fix spelling of VirtualAddrNetwork in man page (thanks tup) r6490@totoro (orig r6489): arma | 2006-05-23 20:21:55 -0400 Claim a commonname of Tor, rather than TOR, in tls handshakes. Maybe this will help us win the war of names. r6491@totoro (orig r6490): arma | 2006-05-23 20:37:38 -0400 Stop initializing the hardware accelerator engines simply because we overloaded the meaning of the argument to crypto_global_init(). r6492@totoro (orig r6491): phobos | 2006-05-24 01:01:29 -0400 Add in the key CFBundleIdentifier required by XCode 2.x and beyond. r6493@totoro (orig r6492): arma | 2006-05-24 07:13:03 -0400 make options->RedirectExit work again; resolve bug 293. r6495@totoro (orig r6494): arma | 2006-05-24 19:03:28 -0400 make cookie authentication for the controller work again, maybe. it sure doesn't now. r6497@totoro (orig r6496): arma | 2006-05-25 16:06:09 -0400 Stop being picky about what the arguments to mapaddress look like. we were refusing names that had $ in them, which people who specify $key.exit will be sad about. There are likely other examples. If people can think of reasons why we should be picky, let me know. r6500@totoro (orig r6499): phobos | 2006-05-26 09:22:20 -0400 SUSEisms to enable "make dist-rpm" functionality in SuSe r6501@totoro (orig r6500): phobos | 2006-05-26 09:32:56 -0400 Use macros in place of details. r6502@totoro (orig r6501): phobos | 2006-05-26 09:42:28 -0400 Let AC_OUTPUT know about contrib/suse/tor.sh and we only need tor.sh from contrib/suse r6503@totoro (orig r6502): arma | 2006-05-26 09:51:20 -0400 build the Makefile in contrib/suse/ too r6504@totoro (orig r6503): arma | 2006-05-26 09:51:45 -0400 resolve an unused variable r6506@totoro (orig r6505): arma | 2006-05-26 12:29:20 -0400 correct a false log message, since we actually reset all our downloading stats every hour, and sometimes more often. r6507@totoro (orig r6506): arma | 2006-05-26 12:29:33 -0400 be more verbose about testing reachability of our ORPort. r6508@totoro (orig r6507): arma | 2006-05-26 12:32:16 -0400 if we're a server and some peer has a broken tls certificate, don't shout about it unless we want to hear about protocol violations. r6509@totoro (orig r6508): arma | 2006-05-28 12:07:44 -0400 clean up a comment r6510@totoro (orig r6509): arma | 2006-05-28 12:14:26 -0400 directory authorities should be more tolerant of failed reachability tests before crying foul to the server operator. r6511@totoro (orig r6510): nickm | 2006-05-28 12:54:39 -0400 Add a basic mmap function, with a "fake-it" wrapper to do read_file_from_str instead. Based on code from Michael Mohr. r6512@totoro (orig r6511): arma | 2006-05-30 01:05:50 -0400 remove a few things from the 0.1.2 todo, and add one r6513@totoro (orig r6512): arma | 2006-05-30 01:29:03 -0400 simplify a log message r6514@totoro (orig r6513): arma | 2006-05-30 02:11:36 -0400 tentative change: if you have your dirport set, you are a directory mirror, whether or not your orport is set. r6515@totoro (orig r6514): arma | 2006-05-30 02:11:46 -0400 and clarify the spec to say this too. r6516@totoro (orig r6515): arma | 2006-05-30 02:17:28 -0400 END_CIRC_REASON_OR_IDENTITY apparently means that we were told to connect to a different OR than lives on the addr:port we connected to. we don't actually remember whether that was the case, currently. so call it END_CIRC_REASON_OR_CONN_CLOSED as a compromise. r6517@totoro (orig r6516): arma | 2006-05-30 02:19:06 -0400 stop fetching descriptors if we're not a dir mirror and we haven't tried to establish any circuits lately. r6518@totoro (orig r6517): arma | 2006-05-30 02:19:48 -0400 and get grammar right r6519@totoro (orig r6518): arma | 2006-05-30 02:23:44 -0400 connection_t kept the identity_pkey but all it did was store it and free it. perhaps we don't need it after all? r6520@totoro (orig r6519): arma | 2006-05-30 02:36:32 -0400 mark off a todo item. i'll put it back if it turns out it doesn't work. r6521@totoro (orig r6520): nickm | 2006-05-30 16:41:22 -0400 Rearrange TODO. r6522@totoro (orig r6521): arma | 2006-06-01 04:43:56 -0400 update the explanation for deprecating v0 control spec. r6523@totoro (orig r6522): arma | 2006-06-02 22:56:44 -0400 don't stop fetching server descriptors if we're a server and haven't found ourselves reachable yet. r6524@totoro (orig r6523): nickm | 2006-06-03 14:52:31 -0400 Patch based on post by Mike C to or-dev; special-case based on use of MSVC, rather than on MS_WINDOWS, so that mingw builds. r6525@totoro (orig r6524): nickm | 2006-06-03 15:49:42 -0400 Add async dns code from Adam Langley, tweaked to build on OSX. Long-term, we may want to switch to libevnet/c-ares, if they ever handle 10k fd situations properly. This one still needs work too, but at least it is small. This code is disabled by default, and not integrated with dns.c. r6526@totoro (orig r6525): nickm | 2006-06-03 16:52:24 -0400 Make dns.c use eventdns.c -- but only when you pass the --enable-eventdns argument to configure.in. This will *so* not work on Windows yet. r6527@totoro (orig r6526): nickm | 2006-06-03 17:41:14 -0400 More DNS fixes. Send meaningful TTLs back to the client when possible. Cache at the server side independently from the TTL, to prevent attackers from probing the server to see who has been asking for what hostnames. (Hi, Dan Kaminski!) Also, clean some whitespace. r6528@totoro (orig r6527): nickm | 2006-06-03 17:47:26 -0400 Oops. When we dont get a TTL, we should default to the default, not to the minimum. r6529@totoro (orig r6528): nickm | 2006-06-03 18:05:23 -0400 Make eventdns.[ch] into good C90; remove signed/unsigned comparisons. r6530@totoro (orig r6529): arma | 2006-06-04 02:16:20 -0400 punctuation and spelling r6531@totoro (orig r6530): arma | 2006-06-04 02:17:32 -0400 if we insist on printing pointer values, at least make it stop complaining on (my particular) 64 bit platform. r6533@totoro (orig r6532): nickm | 2006-06-04 18:42:13 -0400 Add a new warning to our "warn a lot" list: unused parameters. This means we have to explicitly "use" unuseds, but it can catch bugs. (It caught two coding mistakes so far.) r6534@totoro (orig r6533): nickm | 2006-06-04 19:23:53 -0400 Some eventdns.c fixes for windows correctness. More will doubtless be needed, especially around the #includes. r6535@totoro (orig r6534): nickm | 2006-06-04 20:32:31 -0400 Hm. Where did we put that ntohl the last time we were juggling it? (hoop-lah). r6536@totoro (orig r6535): nickm | 2006-06-04 21:59:12 -0400 More eventdns.c patches: use HAVE_ALLOCA_H; print IP addrs as dotted quads. r6537@totoro (orig r6536): arma | 2006-06-05 00:29:03 -0400 bandaid for bug 299. this is still a bug, since we don't initialize for hardware acceleration in certain configurations; but not critical until that is supported. r6539@totoro (orig r6538): arma | 2006-06-05 03:27:48 -0400 Note a bug that causes servers to sometimes never send the pending create cell. Nick, is this a bug? If so, is my fix right? r6540@totoro (orig r6539): arma | 2006-06-05 04:02:04 -0400 remove some unused code (i think) r6541@totoro (orig r6540): arma | 2006-06-05 04:25:02 -0400 simplify some code, since circuit_build_failed() is only called on non-open circuits. r6542@totoro (orig r6541): arma | 2006-06-05 04:58:18 -0400 bugfix: if we are making our first ever connection to any entry guard, then don't mark it down at first. we had this implemented but it was disabled due to a bug. r6544@totoro (orig r6543): arma | 2006-06-05 05:08:10 -0400 simplify code now that libevent considers all sockets pollable. what we really mean now is ">= 0", which is clearer to test for. r6545@totoro (orig r6544): arma | 2006-06-05 05:47:19 -0400 scream louder if you've got a pending circuit for a given addr/port but the intended n_conn digest is wrong. r6546@totoro (orig r6545): arma | 2006-06-05 05:51:29 -0400 whoops, add a man page entry for ProtocolWarnings r6547@totoro (orig r6546): arma | 2006-06-05 06:01:52 -0400 don't tell people that the testing circuit failed if we already consider ourselves reachable. this just confuses them. r6548@totoro (orig r6547): nickm | 2006-06-05 19:01:22 -0400 Try to log useful messages at info and debug about what we are resolving and what answers we are getting wrt eventdns. r6549@totoro (orig r6548): arma | 2006-06-05 20:04:52 -0400 ship the event*.h files too. perhaps this will make my 'make dist' produce a tarball that i can build. r6550@totoro (orig r6549): arma | 2006-06-05 20:05:39 -0400 fix typo r6551@totoro (orig r6550): arma | 2006-06-05 20:06:52 -0400 We got an obscure report of an assert error on a windows Tor server with connection_add being called with socket = -1. The only places I can see where that happen would be if our tor_socketpair succeeds but it hands back negative values for some of its file descriptors. Perhaps this will produce more useful assert errors next time. r6552@totoro (orig r6551): nickm | 2006-06-05 20:12:22 -0400 Also, add a temporary hack to make sure eventdns.c is distributd. r6553@totoro (orig r6552): arma | 2006-06-05 23:33:24 -0400 fix the bug where we sometimes would fail to send some create cells once we'd connected to a(nother) tor server. r6557@totoro (orig r6556): arma | 2006-06-06 22:57:23 -0400 looks like we missed a piece of the 0.1.1.9 paranoia code. hopefully this change is a no-op. r6558@totoro (orig r6557): arma | 2006-06-07 02:10:54 -0400 simplify the tortls api: we only support being a "server", that is, even tor clients do the same sort of handshake. this has been true for years, so it's best to get rid of the stale code. r6559@totoro (orig r6558): arma | 2006-06-07 02:21:11 -0400 and now the exciting part: there is now no such thing as doing a client-only tls, that is, one with no certs. r6560@totoro (orig r6559): arma | 2006-06-07 02:53:43 -0400 the CookieAuthentication section in our spec seems to assume we're still using the v0 control protocol. r6561@totoro (orig r6560): arma | 2006-06-07 03:11:42 -0400 make connection_or_nonopen_was_started_here() based on something less voodooey. it turns out we already do keep a flag like that around. r6562@totoro (orig r6561): arma | 2006-06-07 04:42:24 -0400 put a bandaid in place so servers will have an easier time believing that they're reachable. this may help resolve the servers-on-dynamic-ip-addresses problem. r6564@totoro (orig r6563): arma | 2006-06-07 05:18:53 -0400 re-enable per-connection rate limiting. get rid of the "OP bandwidth" concept. lay groundwork for "bandwidth classes" -- separate global buckets that apply depending on what sort of conn it is. r6566@totoro (orig r6565): arma | 2006-06-08 05:20:58 -0400 ah, that explains why we weren't going dormant with respect to descriptor fetches. maybe now it will work. r6567@totoro (orig r6566): arma | 2006-06-08 05:35:20 -0400 remove a bit more obsolete code r6569@totoro (orig r6568): arma | 2006-06-08 18:36:13 -0400 ok, ok, maybe *this* time my rep_hist_circbuilding_dormant() will work. r6570@totoro (orig r6569): arma | 2006-06-08 22:20:42 -0400 try a better string at the top of torrc's autogenerated torrc. r6571@totoro (orig r6570): arma | 2006-06-08 22:45:39 -0400 fix a bootstrapping check we ignored that prevents us from running with only one dir authority. r6572@totoro (orig r6571): arma | 2006-06-09 02:35:45 -0400 Bandaid for a seg fault i just got in 0.1.1.20. More generally, i reopened bug 222. Whee. r6574@totoro (orig r6573): arma | 2006-06-09 02:52:49 -0400 and forward-port too. r6576@totoro (orig r6575): arma | 2006-06-09 05:02:32 -0400 when only one router is labelled as a guard, and we've already picked him, we would cycle endlessly picking him again, being unhappy about it, and so forth. now we specifically exclude guards when picking a new guard. r6577@totoro (orig r6576): arma | 2006-06-09 05:07:59 -0400 actually, don't fix it that far. we should still do some error checking. r6579@totoro (orig r6578): nickm | 2006-06-09 11:57:58 -0400 Override our notion of printability for esc_for_log. 127 and up are never printable. Take that, locales. r6580@totoro (orig r6579): nickm | 2006-06-09 13:07:22 -0400 Another escape() fix, for picky sprintfs. r6582@totoro (orig r6581): arma | 2006-06-09 20:26:39 -0400 Add a new config option TestVia, that lets you specify preferred middle hops to use for testing circuits. Perhaps this will let me debug the reachability problem better. r6583@totoro (orig r6582): arma | 2006-06-09 20:30:49 -0400 clean up formatting in the man page r6584@totoro (orig r6583): arma | 2006-06-09 20:32:14 -0400 add TestVia to the man page r6586@totoro (orig r6585): arma | 2006-06-09 20:57:12 -0400 take out the reachability bandaid in 0.1.2.x as well. maybe we will actually be able to fix it, instead. r6596@totoro (orig r6595): phobos | 2006-06-10 01:37:17 -0400 Remove echo -n to make start script slightly more readable on boot. r6600@totoro (orig r6599): arma | 2006-06-10 21:41:30 -0400 interim changelog for 0.1.2.1-alpha r6601@totoro (orig r6600): arma | 2006-06-10 21:42:21 -0400 forward-port the 0.1.1.21 changelog. r6604@totoro (orig r6603): weasel | 2006-06-11 20:49:07 -0400 Forward port changelog r6605@totoro (orig r6604): arma | 2006-06-12 02:03:15 -0400 allow people to start their tor with runasdaemon set but with no logs set at all. r6606@totoro (orig r6605): arma | 2006-06-12 06:44:00 -0400 typo, whitespace, and a clarification r6607@totoro (orig r6606): arma | 2006-06-12 07:59:19 -0400 Finally solve the "closing wedged cpuworkers" bug. Woo. This happened when we got two create cells in a row from the same TLS connection. It would hand one to the cpuworker, and then immediately handle the second one -- after it had registered that the first one was busy, but before it had updated the timestamp that we use to decide how *long* it's been busy. r6609@totoro (orig r6608): weasel | 2006-06-12 18:03:25 -0400 Make the Exit tag in status documents actually work in head too r6610@totoro (orig r6609): arma | 2006-06-12 22:48:06 -0400 reintroduce the logic to exit_policy_is_general_exit() to count how many ports are allowd. require two ports open, not just one. r6611@totoro (orig r6610): arma | 2006-06-13 01:36:35 -0400 Fix the bug that was causing servers to not find themselves reachable if they changed IP addresses. This happened because middle servers knew the old descriptor, and kept swapping the addr/port we asked for with the one they thought was right. So the create cell never got sent, because it was asking for a different addr/port than we believed we had connected to. r6614@totoro (orig r6613): arma | 2006-06-13 01:50:24 -0400 harmless typo r6615@totoro (orig r6614): arma | 2006-06-13 01:51:28 -0400 Defense in depth: fix the reachability bug a second way too. Now if we establish a connection with the right digest, regardless of what the addr/port is, and we have pending create cells, use it. r6616@totoro (orig r6615): arma | 2006-06-13 05:16:09 -0400 export the default exit policy via the control port, so controllers don't need to guess what it is / will be later. r6617@totoro (orig r6616): arma | 2006-06-13 06:25:22 -0400 first cut at a workaround for the reachability bug: explicitly find a server running the right version, if we can, and ask for that one. r6618@totoro (orig r6617): arma | 2006-06-13 06:48:26 -0400 bugfix in exit_policy_is_general_exit() that weasel found. this time for sure! r6619@totoro (orig r6618): arma | 2006-06-13 07:11:19 -0400 now we can tell dirserv_dump_directory_to_string() whether we want it to include down/invalid descriptors or not. r6620@totoro (orig r6619): arma | 2006-06-13 08:05:59 -0400 be more lax about recognizing valid hexdigests. r6621@totoro (orig r6620): arma | 2006-06-13 08:57:19 -0400 back off and add the $ at the beginning of the preferrednodes list we generate. r6622@totoro (orig r6621): nickm | 2006-06-13 17:49:56 -0400 eventdns: Apply a couple of patches from AGL; start working on windows compat; note some TODOs. r6623@totoro (orig r6622): arma | 2006-06-14 07:06:43 -0400 bugfix: discourage picking directory authorities as our TestVia hops, even if they're running the right versions, since we probably already have a connection established to them. r6624@totoro (orig r6623): arma | 2006-06-14 18:28:16 -0400 upgrade the severity of the 'clock jump' warn, and ask people to report if it occurs. r6625@totoro (orig r6624): arma | 2006-06-14 19:21:22 -0400 start checking for limits.h too. we should resume compiling on irix64 and other weird platforms now. r6626@totoro (orig r6625): arma | 2006-06-15 05:03:15 -0400 lower the number of seconds before we yell about clock jump. and make the yelling only happen if you're a server. r6627@totoro (orig r6626): arma | 2006-06-15 18:32:00 -0400 fix recommended url in torrc.sample for server sign-up r6628@totoro (orig r6627): weasel | 2006-06-15 18:52:56 -0400 Add a /tor/dir-all-weaselhack directory resource so I do not have to update my scripts r6629@totoro (orig r6628): arma | 2006-06-15 18:59:07 -0400 fix spacing r6630@totoro (orig r6629): weasel | 2006-06-15 19:14:01 -0400 And a minor bugfix to the weaselhack r6631@totoro (orig r6630): weasel | 2006-06-15 19:20:50 -0400 Forward port 07_log_to_file_by_default.dpatch r6632@totoro (orig r6631): arma | 2006-06-15 20:04:46 -0400 clean up man page. expand on contactinfo a bit. r6633@totoro (orig r6632): weasel | 2006-06-15 22:04:04 -0400 Fix configure.in to not produce broken configure files with more recent versions of autoconf. Thanks to Clint for his auto* voodoo. r6634@totoro (orig r6633): nickm | 2006-06-16 11:40:57 -0400 Clarify mmap and memory-use hacks. r6635@totoro (orig r6634): nickm | 2006-06-18 03:21:35 -0400 Add smartlist_reverse and smartlist_pop_last. r6636@totoro (orig r6635): nickm | 2006-06-18 03:22:36 -0400 Add a memdup function to util r6637@totoro (orig r6636): nickm | 2006-06-18 03:24:29 -0400 Add some incremental encryption wrappers to torgzip code r6638@totoro (orig r6637): nickm | 2006-06-18 03:27:47 -0400 Part of incremental encryption logic for buffers: there is a subtle yucky point documented in a comment. r6639@totoro (orig r6638): nickm | 2006-06-18 03:32:31 -0400 perhaps the reason I rail against cut-and-paste programming so vehemently is that I am so bad at it. r6640@totoro (orig r6639): nickm | 2006-06-18 03:35:10 -0400 Add tests for several of the more recently committed functions. r6641@totoro (orig r6640): nickm | 2006-06-18 03:37:21 -0400 remove non-germane comment r6642@totoro (orig r6641): nickm | 2006-06-18 03:38:55 -0400 Instead of adding servers and v1 directories to buffers en masse, directory servers add them on the fly as their outbufs are depleted. This will save ram on busy dirservers. r6643@totoro (orig r6642): nickm | 2006-06-18 03:55:04 -0400 Oops. conn->requested_resource is client only. r6644@totoro (orig r6643): nickm | 2006-06-18 03:57:47 -0400 add coverage for a default case r6645@totoro (orig r6644): nickm | 2006-06-18 04:07:16 -0400 Fix a couple of bugs in last patch. r6646@totoro (orig r6645): nickm | 2006-06-18 04:13:45 -0400 That dir_refresh_src fix will only work if I enable it. r6647@totoro (orig r6646): nickm | 2006-06-18 04:16:05 -0400 And actually check the url when it exists. that might work better. r6648@totoro (orig r6647): nickm | 2006-06-18 04:19:35 -0400 Stop trying to refresh when we are out of data. r6649@totoro (orig r6648): nickm | 2006-06-18 04:21:27 -0400 make zlib buffer function set buf_highwater properly r6650@totoro (orig r6649): nickm | 2006-06-18 04:44:34 -0400 Temporarily disable sentinels on buffers r6651@totoro (orig r6650): nickm | 2006-06-18 04:46:55 -0400 write_to_buf != connection_write_to_buf. Also, add a connection_write_to_buf_zlib wrapper that sucks. r6652@totoro (orig r6651): nickm | 2006-06-18 04:53:09 -0400 Make connection_write_to_buf_zlib set outbuf_flushlen right. r6653@totoro (orig r6652): nickm | 2006-06-18 05:03:48 -0400 Another _zlib fix. r6654@totoro (orig r6653): nickm | 2006-06-18 11:53:54 -0400 Re-enable buffer RAM guard values. r6655@totoro (orig r6654): nickm | 2006-06-18 12:05:54 -0400 Backport candidate: implement the "is this uptime change cosmetic" test properly. r6657@totoro (orig r6656): nickm | 2006-06-18 12:20:38 -0400 another write_to_buf_zlib fix. r6658@totoro (orig r6657): nickm | 2006-06-18 12:39:26 -0400 Ah. That seems to work. r6659@totoro (orig r6658): nickm | 2006-06-18 16:39:46 -0400 Resolve control flow warning. r6660@totoro (orig r6659): nickm | 2006-06-18 16:58:27 -0400 Become capable of noticing that we are done sending a directory. r6661@totoro (orig r6660): arma | 2006-06-18 17:07:45 -0400 correct a function comment in compute_preferred_testing_list() r6663@totoro (orig r6662): nickm | 2006-06-18 17:15:01 -0400 Fix an assert that still isnt the assert we are hunting. r6664@totoro (orig r6663): nickm | 2006-06-18 17:30:03 -0400 Fix a tricky crash: making the_directory heap-allocated (so we could refcount it and have multiple instances as neeeded) means that calls to dirserv_regenerate_directory could invalidate the auth_dir value passed to dirserv_pick_cached_dir_obj. Big fun. r6665@totoro (orig r6664): nickm | 2006-06-19 20:48:23 -0400 Start spooling v2 networkstatus docs as well. r6666@totoro (orig r6665): phobos | 2006-06-20 00:16:46 -0400 First crack at version checking for OSX installer. r6667@totoro (orig r6666): phobos | 2006-06-20 00:18:47 -0400 Minor fix to pre-instllation version check for OSX r6668@totoro (orig r6667): nickm | 2006-06-20 02:27:13 -0400 Ah. We need a new zlib_state for each networkstatus we spool out. r6669@totoro (orig r6668): arma | 2006-06-20 09:14:07 -0400 add a note for nick to fix r6670@totoro (orig r6669): nickm | 2006-06-20 12:48:32 -0400 Fishy, but harmless. r6671@totoro (orig r6670): nickm | 2006-06-20 19:06:52 -0400 Fix bug in networkstatus spooling: spool more than the first networkstatus. r6672@totoro (orig r6671): weasel | 2006-06-20 19:11:15 -0400 <nickm> ooh, that log shouldn't be there. can you take it out? r6673@totoro (orig r6672): phobos | 2006-06-20 22:27:18 -0400 Remove the osx version requirements on install due to a messy Installer situation between pre-panther, tiger and beyond, and metapackages. r6674@totoro (orig r6673): nickm | 2006-06-21 00:57:12 -0400 When requesting or serving resources via fingerprint/digest, request and respond in-order, removing duplicates. r6675@totoro (orig r6674): weasel | 2006-06-21 18:13:03 -0400 Fix 07_log_to_file_by_default in debian head r6676@totoro (orig r6675): nickm | 2006-06-22 03:01:54 -0400 Next batch of memory miserdom: mmap cached-routers file. This is sure to break somewhere. r6677@totoro (orig r6676): nickm | 2006-06-22 03:10:37 -0400 #if out test that was failing because of an extra newline. r6678@totoro (orig r6677): nickm | 2006-06-22 03:19:28 -0400 Set offset properly when parsing cache. r6679@totoro (orig r6678): nickm | 2006-06-22 03:25:15 -0400 Fix a bunch of spaces. r6680@totoro (orig r6679): nickm | 2006-06-22 03:29:14 -0400 Ooh, that could have been bad. Sort digests as digests, not strings. r6681@totoro (orig r6680): nickm | 2006-06-22 03:34:04 -0400 Make some more verbose gcc warnings go away. r6682@totoro (orig r6681): nickm | 2006-06-22 03:49:41 -0400 Mark some more TODO items done. r6683@totoro (orig r6682): nickm | 2006-06-23 22:06:52 -0400 Apparently, zlib sometimes reports Z_BUF_ERROR on input exhaustion as well as on running out of output space. This could well fix the assert bug reported by weasel and arma. r6684@totoro (orig r6683): nickm | 2006-06-23 22:10:21 -0400 Turn a while into a do/while; save a redundant test r6685@totoro (orig r6684): arma | 2006-06-24 00:57:59 -0400 refuse to write an iso_time which we can't parse, when dumping bandwidth state. this fixes the particular incident in bug 308, but the general issue remains. r6686@totoro (orig r6685): phobos | 2006-06-25 00:02:43 -0400 Create binary osx un-installer, update perms on install so anyone can run the uninstaller. r6689@totoro (orig r6688): phobos | 2006-06-25 00:07:24 -0400 Fix the messed up commit. r6690@totoro (orig r6689): phobos | 2006-06-25 00:08:21 -0400 And, commit it correctly. r6693@totoro (orig r6692): arma | 2006-06-27 07:23:10 -0400 specify the dir spec better (suggested by lexi) r6694@totoro (orig r6693): nickm | 2006-06-27 11:52:51 -0400 Likely fix for bug 309: when we calculate offsets after rebuilding the descriptor cache, do not reset the offset pointer half-way through. r6695@totoro (orig r6694): nickm | 2006-06-28 04:54:32 -0400 Actually enable mmap. That should improve matters. r6696@totoro (orig r6695): nickm | 2006-06-28 04:55:53 -0400 Make sure that our calculated offsets for routers is correct; again. This time bug 309 may be gone gone gone. r6697@totoro (orig r6696): nickm | 2006-06-28 04:57:41 -0400 Aaand re-disable the bogus test in get_body() r6698@totoro (orig r6697): nickm | 2006-06-28 07:03:34 -0400 Add a check to try to make cache rebuild fail fast if it is going to fail r6699@totoro (orig r6698): nickm | 2006-06-28 11:36:28 -0400 Fix another idiot bug causing symptom 309. Why cant I program? r6700@totoro (orig r6699): arma | 2006-06-28 11:39:02 -0400 tab-man strikes again r6701@totoro (orig r6700): nickm | 2006-06-29 07:04:42 -0400 complete_only == !allow_partial. This enables useful use of partial desc downloads. Backport candidate r6702@totoro (orig r6701): nickm | 2006-06-29 07:17:36 -0400 Harmless: Z_OK is not an acceptable answer to Z_FINISH. r6703@totoro (orig r6702): nickm | 2006-06-29 07:19:52 -0400 Apparent 311 fix: apparently passing Z_FINISH an empty string is problematic. r6704@totoro (orig r6703): arma | 2006-06-29 09:10:08 -0400 remove the word 'middleman' from the sample torrc r6705@totoro (orig r6704): arma | 2006-06-29 09:11:23 -0400 also remove word 'middleman' from a log notice r6706@totoro (orig r6705): nickm | 2006-06-30 06:50:43 -0400 Unify HTTP response code into one place so it is easier to add headers. Add an X-You-Are header, which we should probably rename. NOTE that we should not use this field for things where it matters if dirs lie. r6707@totoro (orig r6706): nickm | 2006-06-30 06:52:12 -0400 Add a .cvsignore file for contrib/suse/ r6708@totoro (orig r6707): nickm | 2006-07-01 17:51:21 -0400 Oops. Headers work better when named right. r6710@totoro (orig r6709): arma | 2006-07-03 23:19:59 -0400 minor fixes r6711@totoro (orig r6710): arma | 2006-07-03 23:25:07 -0400 minor fixes r6712@totoro (orig r6711): arma | 2006-07-03 23:27:09 -0400 No longer permit create cells to have the wrong circ_id_type. No running Tors should still have this bug. r6713@totoro (orig r6712): arma | 2006-07-03 23:31:27 -0400 Get rid of the router_retry_connections notion. Now routers no longer try to rebuild long-term connections to directory authorities, and directory authorities no longer try to rebuild long-term connections to all servers. We still don't hang up connections in these two cases though -- we need to look at it more carefully to avoid flapping, and we likely need to wait til 0.1.1.x is obsolete. r6714@totoro (orig r6713): arma | 2006-07-03 23:33:17 -0400 touch up the TODO and HACKING files r6715@totoro (orig r6714): arma | 2006-07-03 23:39:01 -0400 mention in the tor dmg instructions that you may need to remove your old shared library libevent, lest your linker get confused. r6716@totoro (orig r6715): arma | 2006-07-03 23:40:45 -0400 a first attempt at specifying HELLO cells. plus general cleanup on tor-spec. r6717@totoro (orig r6716): arma | 2006-07-04 11:51:59 -0400 if we're the server-side of the tls and there are problems, don't yell as loudly. r6718@totoro (orig r6717): arma | 2006-07-04 11:52:22 -0400 fix a misleading function comment r6720@totoro (orig r6719): arma | 2006-07-04 12:07:49 -0400 name the HELLO version the "link version" r6721@totoro (orig r6720): arma | 2006-07-04 12:11:35 -0400 ok, i'm not allowed to say that there. oh well. r6723@totoro (orig r6722): arma | 2006-07-04 14:18:08 -0400 Make the X-You-Are header more accurate when there's a proxy in the middle. r6727@totoro (orig r6726): arma | 2006-07-04 16:25:17 -0400 oops, we were ignoring options->ExcludeNodes when picking entry guards. it is still the case that we ignore it with respect to entry guards that we've already picked. r6728@totoro (orig r6727): arma | 2006-07-05 14:19:42 -0400 actually, that excludenodes fix was redundant. take it out. r6729@totoro (orig r6728): nickm | 2006-07-05 17:28:37 -0400 Add plausile logging support to eventdns; stop putting stuff onto stdout. r6730@totoro (orig r6729): nickm | 2006-07-05 17:33:46 -0400 Spelling fix. r6731@totoro (orig r6730): nickm | 2006-07-05 17:42:18 -0400 Clean up eventdns messages. r6734@totoro (orig r6733): arma | 2006-07-05 22:44:07 -0400 when an exit node gets a malformed begin cell, don't complain to the node operator, since he can't do anything about it. r6735@totoro (orig r6734): arma | 2006-07-05 22:45:46 -0400 whitespace/tab fixes r6736@totoro (orig r6735): arma | 2006-07-05 23:05:01 -0400 rename X-You-Are to something slightly better. r6737@totoro (orig r6736): phobos | 2006-07-06 11:51:07 -0400 Update osx binary un-installer naming, remove invalid osx binary un-installer app r6739@totoro (orig r6738): arma | 2006-07-06 12:19:00 -0400 forward-port the 0.1.1.22 changelog, minus the line about the osx uninstaller. r6740@totoro (orig r6739): phobos | 2006-07-06 12:28:19 -0400 Remove osx binary uninstaller changes. Tiger and Panther won't play nice in the same way. r6742@totoro (orig r6741): phobos | 2006-07-06 20:54:39 -0400 Binary OSX un-installer tarball r6743@totoro (orig r6742): phobos | 2006-07-06 20:55:12 -0400 Updated OSX binary un-installer for testing. r6744@totoro (orig r6743): nickm | 2006-07-07 13:31:56 -0400 eventdns: check for malloc() failures. r6745@totoro (orig r6744): nickm | 2006-07-07 13:33:30 -0400 When using eventdns: suppress logging of addresses when SafeLogging is active, and make set of nameservers configurable from torrc. r6746@totoro (orig r6745): nickm | 2006-07-07 15:08:44 -0400 Spellcheck and remove spurious include in eventdns.c r6747@totoro (orig r6746): arma | 2006-07-08 13:38:46 -0400 Fix a crash if you enable FascistFirewall but not FirewallPorts. Reported by Frediano Ziglio. r6749@totoro (orig r6748): nickm | 2006-07-09 18:28:12 -0400 First part of making mmap-based stuff work on win32: save descriptors as "binary" (no LF->CRLF tanslation) so that we can mmap them properly later. Patch from Frediano Ziglio. r6750@totoro (orig r6749): nickm | 2006-07-09 18:29:12 -0400 Make compilation work on old MSVCs without GetVertsionEx magic. Patch from Frediano Ziglio. r6751@totoro (orig r6750): nickm | 2006-07-09 18:33:21 -0400 Fix project file for MSVC6 (!). Patch from Frediano Ziglio. r6752@totoro (orig r6751): nickm | 2006-07-10 14:38:57 -0400 Add a const; fix a (probably harmless) bug when storing a resolve we forgot we asked for. r6753@totoro (orig r6752): nickm | 2006-07-10 23:33:16 -0400 OR_CONN_EVENT_NEW: we should probably handle that , should we not? Especially since 23:26 < phobos> nickm: grep -c "Unrecognized status code 4" tor.log r6754@totoro (orig r6753): phobos | 2006-07-11 00:27:12 -0400 First crack at launchd plist for Tor r6755@totoro (orig r6754): phobos | 2006-07-11 00:37:05 -0400 Add in start parameters. Perhaps we should ship these set correct in the default torrc. r6756@totoro (orig r6755): phobos | 2006-07-11 16:51:06 -0400 Move cmd line parameters into the config file, remove chroot, and satisfy the requirement of a ProgramArgument array r6757@totoro (orig r6756): nickm | 2006-07-11 16:51:58 -0400 Add some debugging asserts to dns.c; these are too expensive to leave in permanently. r6758@totoro (orig r6757): phobos | 2006-07-12 18:09:21 -0400 Update config to redirect output to the tor log file for now. r6759@totoro (orig r6758): arma | 2006-07-13 23:14:02 -0400 Avoid an integer underflow when the dir authority decides whether a router is stable: we might wrongly label it stable, and compute a slightly wrong median stability, when a descriptor is published later than now. Inspired by Matt's Vidalia checkin: http://trac.vidalia-project.net/changeset/1074 r6761@totoro (orig r6760): arma | 2006-07-15 01:49:57 -0400 minor tweak on the dir spec r6762@totoro (orig r6761): arma | 2006-07-15 01:50:22 -0400 mention the existence of dir-spec in tor-spec, and note that we need to update it. r6763@totoro (orig r6762): arma | 2006-07-15 01:53:41 -0400 add a whole lot more work to the todo. r6764@totoro (orig r6763): arma | 2006-07-15 15:21:30 -0400 stick to nick's nul/null convention r6765@totoro (orig r6764): arma | 2006-07-15 16:26:05 -0400 parameterize the loudness of resolve_my_address(), and call things IP addresses, not IPs. r6766@totoro (orig r6765): arma | 2006-07-16 01:57:11 -0400 whitespace/etc cleanups r6767@totoro (orig r6766): phobos | 2006-07-16 09:58:10 -0400 Change the way Tor starts on OSX 10.4 vs pre-10.4. 10.4 Tor now uses launchd for current and forward compatibility. r6768@totoro (orig r6767): phobos | 2006-07-16 14:18:40 -0400 Remove a merged if-then from line 85 r6769@totoro (orig r6768): nickm | 2006-07-16 20:39:05 -0400 MSVC6 is apparently terrified of unnatural cross-breeding between uint64_t and double, and needs more persuasion than usual to cast one to the other. Issue identified by Frediano Ziglio; patch revised for minimal impact on non-MSVC6 compilers. r6770@totoro (orig r6769): phobos | 2006-07-16 23:57:21 -0400 Added net.freehaven.tor.plist to AC_OUTPUT r6771@totoro (orig r6770): phobos | 2006-07-17 00:31:22 -0400 Minor fixed for launchd xml plist r6772@totoro (orig r6771): arma | 2006-07-17 01:12:54 -0400 nick suggests that the hello cell should have both server IP and client IP. he's right. r6773@totoro (orig r6772): arma | 2006-07-17 02:20:09 -0400 fix wordo r6774@totoro (orig r6773): arma | 2006-07-17 02:26:19 -0400 we are constrained more than we realized, on what g^x values we can accept or refuse. r6775@totoro (orig r6774): arma | 2006-07-17 02:35:06 -0400 Allow servers with no hostname or IP address to learn their IP address by asking the directory authorities. This code only kicks in when you would normally have exited with a "no address" error. This design is flawed, though, since the X-Your-Address-Is header is not authenticated, and doing it this way introduces too many new attacks. The right answer is to give IP address hints inside the HELLO cell; much of this code can be reused when we switch. r6776@totoro (orig r6775): arma | 2006-07-17 02:54:28 -0400 fix some more places where we shouldn't crash if we can't build our own descriptor yet. r6778@totoro (orig r6777): arma | 2006-07-17 02:59:56 -0400 and don't try to build the descriptor every second, if it's dirty but we don't have a known address. r6779@totoro (orig r6778): arma | 2006-07-17 04:11:27 -0400 huge bugfix: we weren't ever writing an http header when sending out network statuses! so clients were downloading the whole thing, and then discarding them because they're malformed. r6780@totoro (orig r6779): arma | 2006-07-17 04:17:51 -0400 Make a louder statement the first time we learn a guessed IP address. r6781@totoro (orig r6780): arma | 2006-07-17 15:33:54 -0400 parameterize the loudness of log_addr_has_changed(), since it's the only place where we inform the user of a new IP address, if we're guessing it from external sources. r6782@totoro (orig r6781): arma | 2006-07-17 15:42:22 -0400 shuffle todo items r6783@totoro (orig r6782): arma | 2006-07-17 20:01:12 -0400 pick a log domain; resolve an xxxx r6784@totoro (orig r6783): arma | 2006-07-17 20:59:46 -0400 If we are using an exit enclave and we can't connect, e.g. because its webserver is misconfigured to not listen on localhost, then back off and try connecting from somewhere else before we fail. r6785@totoro (orig r6784): nickm | 2006-07-17 22:01:32 -0400 Hm. We probably should define INT64_MAX if we really want it. (Especially since we only want it on one platform, where, coincidentally, it is not defined.) r6786@totoro (orig r6785): nickm | 2006-07-17 22:24:01 -0400 Oh. And apparently, msvc6 doesnt think very much of doing u64-and-double arithmetic either. r6787@totoro (orig r6786): arma | 2006-07-17 23:06:12 -0400 Start publishing one minute or so after we find our ORPort to be reachable. This will help reduce the number of descriptors we have for ourselves floating around, since it's quite likely other things (e.g. DirPort) will change during that minute too. r6788@totoro (orig r6787): arma | 2006-07-17 23:06:55 -0400 when we find our dirport to be reachable, mark our descriptor dirty so we'll tell the world. (fixes bug 306 reported by pnx) r6789@totoro (orig r6788): phobos | 2006-07-18 00:37:43 -0400 Add the output of OSX arch into the package name in preparation for ppc vs x86 packages. r6790@totoro (orig r6789): phobos | 2006-07-18 00:40:02 -0400 Load and start tor in launchd at end of installation r6791@totoro (orig r6790): arma | 2006-07-18 00:42:32 -0400 Define a schedule for how long to wait between retrying application connections. Rather than waiting a fixed amount of time between each retry, we wait only 5 seconds for the first, 10 seconds for the second, and 15 seconds for each retry after that. Hopefully this will improve the expected experience. Addresses bug 297. r6792@totoro (orig r6791): arma | 2006-07-18 00:48:59 -0400 don't squeal if the first few retries fail. r6793@totoro (orig r6792): nickm | 2006-07-20 12:47:35 -0400 Fork off v0 of the protocol spec; we are going to add versioning soon so we can make backward-incompatible changes without breaking the whole network. Also, fork the v0 directory protocol into its own document, and turn dir-spec.txt into the present tense. r6794@totoro (orig r6793): nickm | 2006-07-20 12:48:02 -0400 Add a few more paragraphs to path-spec.txt r6795@totoro (orig r6794): nickm | 2006-07-20 13:35:54 -0400 Document HELLO cells and proposed connection protocol versioning scheme. NOTE: This will not work as documented; see notes. r6796@totoro (orig r6795): arma | 2006-07-20 19:33:11 -0400 tweak r6797@totoro (orig r6796): arma | 2006-07-20 19:45:26 -0400 tweak r6798@totoro (orig r6797): phobos | 2006-07-21 00:30:19 -0400 Grammar fixes for clarity. r6799@totoro (orig r6798): arma | 2006-07-21 03:06:18 -0400 an entry guard that is "unlisted", as well as not known to be "down", is not therefore "up". r6800@totoro (orig r6799): arma | 2006-07-21 03:53:21 -0400 tweak r6801@totoro (orig r6800): arma | 2006-07-21 03:55:35 -0400 bugfix: if you find yourself reachable, then don't ever make any client requests (so you stop predicting circuits), then hup, then later your IP changes, you won't think circuits are working so you won't try to test reachability, so you won't publish. r6803@totoro (orig r6802): nickm | 2006-07-21 10:53:23 -0400 Another MSVC6 fix. Grnk. r6804@totoro (orig r6803): nickm | 2006-07-21 18:02:58 -0400 These asserts will either cause spurious crashes or help debug the pend->conn->s == -1 issue. r6805@totoro (orig r6804): arma | 2006-07-22 01:29:31 -0400 more bulletproof reachability testing r6806@totoro (orig r6805): arma | 2006-07-22 03:15:34 -0400 i lied, that won't work at all. maybe this will. r6807@totoro (orig r6806): arma | 2006-07-22 03:19:11 -0400 think harder about my logic r6809@totoro (orig r6808): arma | 2006-07-23 01:18:29 -0400 whitespace and docs r6810@totoro (orig r6809): arma | 2006-07-23 01:19:31 -0400 more todo items r6811@totoro (orig r6810): nickm | 2006-07-23 01:32:35 -0400 Add a mem_is_zero function (I think we will need this) and a STRUCT_OFFSET macro (we already need this). r6812@totoro (orig r6811): nickm | 2006-07-23 01:33:10 -0400 Remove STRUCT_OFFSET from config.c r6813@totoro (orig r6812): nickm | 2006-07-23 01:39:37 -0400 Add (void) lines for unused parameters in eventdns.c r6814@totoro (orig r6813): nickm | 2006-07-23 01:40:24 -0400 Delete trailing whitespace in eventdns.c r6815@totoro (orig r6814): arma | 2006-07-23 01:52:27 -0400 use tor_mem_is_zero() in more places. r6816@totoro (orig r6815): arma | 2006-07-23 02:41:02 -0400 rewrite conn->address for GET commands as well as POST commands. r6817@totoro (orig r6816): nickm | 2006-07-23 03:19:49 -0400 Whitespace fix r6818@totoro (orig r6817): nickm | 2006-07-23 03:37:35 -0400 Don't tell anybody, but we're going OO here. This patch splits circuit_t into origin_circuit_t and or_circuit_t. I fixed some segaults; there may be more. We still need to move more rendezvous stuff into subtypes. This is a trial run for splitting up connection_t; if the approach is insane, please say so soon so we can do something smarter. Also, this discards the old HALF_OPEN code, which nobody seems to want. r6819@totoro (orig r6818): nickm | 2006-07-23 04:13:45 -0400 Fix another segfault in assert_circuit_ok. r6820@totoro (orig r6819): arma | 2006-07-23 07:50:03 -0400 publish a new descriptor after we hup. this is important if our config has changed such that we'll want to start advertising our dirport now, etc. r6821@totoro (orig r6820): phobos | 2006-07-23 08:52:06 -0400 These settings are required for those using launchd in OSX 10.4. This is a crude but functional way to insert them for now. r6822@totoro (orig r6821): weasel | 2006-07-23 23:24:25 -0400 Previously our defaults for DataDirectory, PidFile, RunAsDaemon, and Log differed from upstreams. Now Tor behaves just like before (with our own DataDirectory and all) only when run as the debian-tor user. If invoked as any other user, Tor will behave just like the pristine upstream version. r6823@totoro (orig r6822): weasel | 2006-07-23 23:38:26 -0400 Tell users about the init script when they try to run Tor as root. Should we also do this when they try to run their Tor as any other (non root, non debian-tor) user? - add 11_tor_as_root_more_helpful r6892@totoro (orig r6891): weasel | 2006-07-24 21:00:48 -0400 Remove .cvsignore files from trunk r6894@totoro (orig r6893): nickm | 2006-07-24 21:13:04 -0400 Add an item to the TODO. r6899@totoro (orig r6898): nickm | 2006-07-25 00:34:14 -0400 Remove code to special-case "-cvs" ending, since it has not actually mattered since 0.0.9. Perhaps we can special-case even more... r6900@totoro (orig r6899): nickm | 2006-07-25 18:26:42 -0400 Add libor.a and libor-crypto.a to svn:ignore r6901@totoro (orig r6900): nickm | 2006-07-25 18:30:50 -0400 Allow wide lines if they have svn id tags in them. (This matters for svk: those tags can be *big*.) r6902@totoro (orig r6901): nickm | 2006-07-25 18:33:57 -0400 Apply checkSpace.pl to checkSpace.pl. r6903@totoro (orig r6902): nickm | 2006-07-25 18:51:51 -0400 Clarify a TODO, and test tweaked commit-email.pl script. r6904@totoro (orig r6903): nickm | 2006-07-26 15:05:34 -0400 r6902@Kushana: nickm | 2006-07-25 17:30:27 -0400 Move rend_query to origin_circuit_t where it belongs; save another 17 bytes per OR circuit. r6905@totoro (orig r6904): nickm | 2006-07-26 15:05:41 -0400 r6903@Kushana: nickm | 2006-07-25 18:22:48 -0400 No circuit can be both an intro point and a rend point, so we can merge both the cookie and the pk digest into one "rend_token" field for or circuits. This saves another 20 bytes per or circuit. r6906@totoro (orig r6905): nickm | 2006-07-26 15:07:23 -0400 r6907@Kushana: nickm | 2006-07-25 19:03:43 -0400 Realign circuit structs to avoid wasted space. r6907@totoro (orig r6906): nickm | 2006-07-26 15:07:26 -0400 r6908@Kushana: nickm | 2006-07-26 12:38:52 -0400 Refactor connection_t into edge, or, dir, control, and base subtypes. This might save some RAM on busy exit servers, but really matters most in terms of correctness. r6908@totoro (orig r6907): nickm | 2006-07-26 15:07:37 -0400 r6909@Kushana: nickm | 2006-07-26 13:05:58 -0400 Clean up wide lines from last patch. r6909@totoro (orig r6908): nickm | 2006-07-26 15:29:30 -0400 r6918@Kushana: nickm | 2006-07-26 15:22:28 -0400 Fix compilation for eventdns dns.c with split structs. r6910@totoro (orig r6909): nickm | 2006-07-26 15:39:47 -0400 r6920@Kushana: nickm | 2006-07-26 15:39:40 -0400 Mark some TODO items done r6911@totoro (orig r6910): phobos | 2006-07-26 17:50:27 -0400 Set Soft & Hard resource limits to appease launchd. r6912@totoro (orig r6911): phobos | 2006-07-26 19:52:59 -0400 OSX pre-install script to clean up Tor and force a fresh install, but save the server keys if they exist. r6913@totoro (orig r6912): phobos | 2006-07-26 20:19:36 -0400 Be better at finding the Tor install path, backup all of Tor just in case, then blow Tor away r6914@totoro (orig r6913): phobos | 2006-07-26 21:20:02 -0400 Backup only what is needed, write the file we created to a temp file for TorPostFlight to restore the data and remove the temp files r6915@totoro (orig r6914): phobos | 2006-07-26 23:02:47 -0400 The whole process works from preflight to postflight creating a clean Tor install with proper config file edits r6916@totoro (orig r6915): phobos | 2006-07-26 23:10:23 -0400 OSX gets confused when you have two ways to start the same program. r6917@totoro (orig r6916): phobos | 2006-07-26 23:17:50 -0400 Oops, forgot the all important 'r' r6919@totoro (orig r6918): nickm | 2006-07-27 00:10:51 -0400 Fix comments that implied that only dir connections had a purpose field, and the code that believed in those comments. r6920@totoro (orig r6919): nickm | 2006-07-27 01:03:57 -0400 r6922@Kushana: nickm | 2006-07-26 16:32:24 -0400 Rename some fields, compress a bitfield, and document some structs and fields r6923@totoro (orig r6922): nickm | 2006-07-27 13:16:10 -0400 Add more asserts in dns_found_answer. This may confirm my theory that dns_purge_resolve is the culprit. r6924@totoro (orig r6923): phobos | 2006-07-27 13:19:32 -0400 Far better test and handling of existing torrc r6925@totoro (orig r6924): nickm | 2006-07-27 13:37:37 -0400 Get better numbers out of HT_REP_OK r6926@totoro (orig r6925): nickm | 2006-07-27 14:35:25 -0400 Fix a bug in HT_REMOVE. r6927@totoro (orig r6926): nickm | 2006-07-27 14:35:56 -0400 More asserts in dns.c r6928@totoro (orig r6927): arma | 2006-07-27 15:35:11 -0400 resolve typo r6929@totoro (orig r6928): phobos | 2006-07-27 16:03:09 -0400 fi, fy fo fum, if-then- doesn't work without one r6930@totoro (orig r6929): phobos | 2006-07-27 16:24:53 -0400 Forget leopard. r6933@totoro (orig r6932): phobos | 2006-07-28 09:52:36 -0400 So long, farewell, auf Wiedersehen, adieu, launchd r6934@totoro (orig r6933): nickm | 2006-07-28 11:11:11 -0400 r6948@Kushana: nickm | 2006-07-28 10:10:35 -0400 Identify some likely target fields for lowering; lower global_identifier (since we only use it for AP streams and origin circs). r6935@totoro (orig r6934): nickm | 2006-07-28 11:11:20 -0400 r6949@Kushana: nickm | 2006-07-28 10:17:38 -0400 Shave another 8 bytes from connection_t: turn inbuf_reached_eof into a bit, and lower timestamp_lastempty to or_connection_t r6936@totoro (orig r6935): nickm | 2006-07-28 11:11:28 -0400 r6950@Kushana: nickm | 2006-07-28 10:32:08 -0400 Document split fields better. Now, I think we can take a break from type splitting for a bit. r6937@totoro (orig r6936): phobos | 2006-07-29 23:32:54 -0400 Remove the launchd plist file. r6938@totoro (orig r6937): arma | 2006-07-29 23:34:44 -0400 a bit more debugging for phobos r6939@totoro (orig r6938): phobos | 2006-07-29 23:53:18 -0400 Remove the last vestiges of launchd plist. r6940@totoro (orig r6939): arma | 2006-07-30 00:32:58 -0400 defense in depth r6945@totoro (orig r6944): arma | 2006-07-30 00:45:59 -0400 forward-port the website hack. note that with svn, our build system seems to build in-place, so the website/ and img/ directories actually get created in my sandbox. poo. r6946@totoro (orig r6945): arma | 2006-07-30 00:54:13 -0400 fix assert found by DreadWingKnight: now that rendezvous streams are attached to p_streams, the p_streams list can consist of both AP and EXIT conns. r6947@totoro (orig r6946): arma | 2006-07-30 01:36:17 -0400 forward-port the 0.1.1.23 changelog. r6953@totoro (orig r6952): nickm | 2006-07-31 13:59:11 -0400 r6952@Kushana: nickm | 2006-07-28 11:09:37 -0400 Add completely untested find-my-nameservers code for win32. r6954@totoro (orig r6953): nickm | 2006-07-31 13:59:37 -0400 r6958@Kushana: nickm | 2006-07-29 18:54:15 -0400 Looks like we might need a priority queue. r6955@totoro (orig r6954): nickm | 2006-07-31 14:00:18 -0400 r6959@Kushana: nickm | 2006-07-29 22:33:18 -0400 start restructuring dns to use priority queues for expiring entries. r6956@totoro (orig r6955): nickm | 2006-07-31 14:00:47 -0400 r6957@totoro (orig r6956): nickm | 2006-07-31 14:01:18 -0400 r6977@Kushana: nickm | 2006-07-31 13:01:28 -0400 Solve timing-out pending connections. Add pending resolves to expiry queue; when we find an answer, change the pending resolve to "done" and stick the actual answer in the expiry queue as a new entry. This uses a little more memory, but makes the code simpler than other solutions. r6958@totoro (orig r6957): nickm | 2006-07-31 14:01:22 -0400 r6978@Kushana: nickm | 2006-07-31 13:16:14 -0400 Add isupper and islower wrappers to compat.h r6959@totoro (orig r6958): nickm | 2006-07-31 14:01:27 -0400 r6979@Kushana: nickm | 2006-07-31 13:16:58 -0400 Add assert_ok functions for strmap and digestmap; use them in unit test code. r6960@totoro (orig r6959): nickm | 2006-07-31 14:01:37 -0400 r6980@Kushana: nickm | 2006-07-31 13:18:22 -0400 Add a utility function to verify that a string has been through strlower. r6961@totoro (orig r6960): nickm | 2006-07-31 14:01:45 -0400 r6981@Kushana: nickm | 2006-07-31 13:23:26 -0400 More asserts for cache correctness. r6962@totoro (orig r6961): nickm | 2006-07-31 14:01:49 -0400 r6982@Kushana: nickm | 2006-07-31 13:47:19 -0400 documentation and naming tweaks in dns.c r6963@totoro (orig r6962): nickm | 2006-07-31 16:19:58 -0400 r6993@Kushana: nickm | 2006-07-31 16:19:21 -0400 Interesting how much a ! can change the behavior of an assert. r6964@totoro (orig r6963): arma | 2006-07-31 16:25:57 -0400 more compile options mean more codepaths r6965@totoro (orig r6964): arma | 2006-08-01 00:08:15 -0400 man, our sample torrc sucked. r6966@totoro (orig r6965): arma | 2006-08-02 01:17:22 -0400 explain that the exitlist isn't perfect, because some tor exit nodes don't exit on their advertised address. r6969@totoro (orig r6968): weasel | 2006-08-02 21:50:10 -0400 r8207@galaxy: weasel | 2006-08-03 03:22:17 +0200 Merge in local revisions 7944, 8205, and 8206: Forward port 0.1.1.x changelog to trunk r6970@totoro (orig r6969): arma | 2006-08-03 00:22:25 -0400 fix a seg fault on exit for clients; and fix a comment. r6971@totoro (orig r6970): arma | 2006-08-03 00:23:45 -0400 turn future seg faults into asserts r6972@totoro (orig r6971): nickm | 2006-08-03 03:46:25 -0400 Patch from Frediano Ziglio: Windows compilation fixes on eventdns.c. r6973@totoro (orig r6972): nickm | 2006-08-04 14:23:56 -0400 r6995@Kushana: nickm | 2006-07-31 13:30:42 -0700 Avoid segfault if we exit before we get our first dns answer. r6974@totoro (orig r6973): nickm | 2006-08-04 14:24:13 -0400 r6975@totoro (orig r6974): nickm | 2006-08-04 14:24:25 -0400 r7007@Kushana: nickm | 2006-08-03 09:58:30 -0700 Export and use eventdns_config_windows_nameservers(); clean up some comments and log messages. r6976@totoro (orig r6975): nickm | 2006-08-04 14:24:41 -0400 r7008@Kushana: nickm | 2006-08-03 10:03:39 -0700 Oops. We shouldnt initialize eventdns when we are not being a server and not resolving anything. r6977@totoro (orig r6976): nickm | 2006-08-04 14:26:13 -0400 r7009@Kushana: nickm | 2006-08-03 10:44:58 -0700 Add functions to eventdns to allow detecting whether we have any nameservers configured, and to change the list of nameservers after initial configuration. r6978@totoro (orig r6977): nickm | 2006-08-04 14:26:40 -0400 r7010@Kushana: nickm | 2006-08-03 10:47:36 -0700 Enable log message format checking in eventdns.c when __GNUC__ is defined. r6979@totoro (orig r6978): nickm | 2006-08-04 14:27:10 -0400 r7011@Kushana: nickm | 2006-08-03 13:26:34 -0700 eventdns: Document functions added to API; make suspended requests go to the front of the queue; check (or explicitly ignore) return values on libevent functions. r6980@totoro (orig r6979): nickm | 2006-08-04 14:31:13 -0400 r6981@totoro (orig r6980): nickm | 2006-08-04 14:32:43 -0400 r7012@Kushana: nickm | 2006-08-03 19:21:25 -0700 Add an "mmap handle" type to encapsulate bookkeeping elements of mmap issues; add prelim win32 impl r6982@totoro (orig r6981): nickm | 2006-08-04 15:03:40 -0400 r7025@Kushana: nickm | 2006-08-04 12:03:22 -0700 Finish (I hope) windows mmap impl. r6983@totoro (orig r6982): arma | 2006-08-04 16:30:45 -0400 make svn trunk link again. nick can fix this if it's wrong. r6985@totoro (orig r6984): arma | 2006-08-04 23:08:56 -0400 a potential fix on the HELLO protocol design r6986@totoro (orig r6985): nickm | 2006-08-05 13:52:51 -0400 r7027@Kushana: nickm | 2006-08-04 13:06:48 -0700 Oops. Fix downcast macro. r6987@totoro (orig r6986): nickm | 2006-08-05 13:53:08 -0400 r7028@Kushana: nickm | 2006-08-04 13:10:16 -0700 Make data and size fields visible in tor_mmap_t; hide win magic differently. r6988@totoro (orig r6987): nickm | 2006-08-05 13:53:21 -0400 r7029@Kushana: nickm | 2006-08-04 14:08:41 -0700 Remove now-spurious size and data arguments from tor_mmap_file r6989@totoro (orig r6988): nickm | 2006-08-05 13:53:32 -0400 r7030@Kushana: nickm | 2006-08-04 14:46:52 -0700 Close an fd leak on failed mmap() r6990@totoro (orig r6989): phobos | 2006-08-07 21:42:52 -0400 Update preflight to save Privoxy configs as well r6991@totoro (orig r6990): arma | 2006-08-08 02:21:52 -0400 three more todo items r6992@totoro (orig r6991): arma | 2006-08-08 18:56:26 -0400 change the dir-spec to say that it's version 2 of the dir spec, and move the v0 file to v1. r6996@totoro (orig r6995): nickm | 2006-08-08 20:58:27 -0400 Say more about reverse DNS r6999@totoro (orig r6996): nickm | 2006-08-09 02:41:29 -0400 r7056@Kushana: nickm | 2006-08-08 23:40:53 -0700 Add a comment about v0 fallback approach. Why did we dislike discriminating on X.509 certs again? r7000@totoro (orig r6997): nickm | 2006-08-09 02:54:02 -0400 r7058@Kushana: nickm | 2006-08-08 23:53:46 -0700 Clarify point about certs. We have been over this before, but it seems simpler than what we are considering now. r7001@totoro (orig r6998): nickm | 2006-08-09 04:23:27 -0400 r7285@Kushana: nickm | 2006-08-09 01:23:11 -0700 Claim a TODO item (and test commit signing.) r7002@totoro (orig r6999): nickm | 2006-08-09 04:30:11 -0400 r7286@Kushana: nickm | 2006-08-09 01:28:27 -0700 Claim another TODO item (and test commit signing.) r7003@totoro (orig r7000): weasel | 2006-08-09 06:25:01 -0400 r8245@danube: weasel | 2006-08-09 12:24:00 +0200 Add a few comments so I find the rationale for the autoconf --build/--host split in the future when I need it again r7006@totoro (orig r7001): nickm | 2006-08-09 17:42:38 -0400 r7005@totoro: nickm | 2006-08-09 17:42:18 -0400 Begin committing violence against the spec; add some TODO items at the top. Arma, if you disagree, better say so. r7228@totoro (orig r7002): phobos | 2006-08-09 22:06:35 -0400 Update for universal binaries for OSX. r7229@totoro (orig r7003): nickm | 2006-08-10 03:39:47 -0400 Recommend libevent 1.1b for kqueue and win32 methods; deprecate libevent 1.0b harder; make libevent recommendation system saner. r7230@totoro (orig r7004): nickm | 2006-08-10 04:00:13 -0400 Experimentally re-enable kqueue on OSX when using libevent 1.1b or later. Log when we are doing this, so we can diagnose it when it fails. r7231@totoro (orig r7005): arma | 2006-08-10 04:00:54 -0400 a way to make tor more stable in crummy situations r7232@totoro (orig r7006): arma | 2006-08-10 04:13:41 -0400 initial skeleton for issues to resolve re: blocking resistance. r7233@totoro (orig r7007): nickm | 2006-08-10 05:01:37 -0400 r7299@Kushana: nickm | 2006-08-10 01:08:58 -0700 Patch from Tup to add support for transparent AP connections: this basically bundles the functionality of trans-proxy-tor into the tor mainline. Now hosts with compliant pf/netfilter implementations can redirect TCP connections straight to Tor without diverting through SOCKS. r7234@totoro (orig r7008): nickm | 2006-08-10 05:01:46 -0400 r7300@Kushana: nickm | 2006-08-10 01:36:40 -0700 Distinguish netfilter vs pf at configure time based on headers, not on OS. r7235@totoro (orig r7009): nickm | 2006-08-10 05:01:54 -0400 r7301@Kushana: nickm | 2006-08-10 01:41:27 -0700 Only open /dev/pf once. r7236@totoro (orig r7010): nickm | 2006-08-10 05:02:02 -0400 r7302@Kushana: nickm | 2006-08-10 01:48:44 -0700 Warn about open TransListenAddress values. r7237@totoro (orig r7011): nickm | 2006-08-10 05:02:12 -0400 r7303@Kushana: nickm | 2006-08-10 01:52:19 -0700 whitespace fixes r7238@totoro (orig r7012): nickm | 2006-08-10 05:02:26 -0400 r7304@Kushana: nickm | 2006-08-10 01:58:05 -0700 Fix verbose compilation errors; make sure transparent proxy fails when no method is configured. r7240@totoro (orig r7013): nickm | 2006-08-10 05:14:57 -0400 r7313@Kushana: nickm | 2006-08-10 02:13:35 -0700 Add missing .h to header file name. My bad. r7243@totoro (orig r7015): nickm | 2006-08-10 05:30:25 -0400 r7315@Kushana: nickm | 2006-08-10 02:30:13 -0700 add missing "test" and missing include. r7248@totoro (orig r7018): nickm | 2006-08-10 15:56:10 -0400 Fix crash in first-time option validation. Oops. r7249@totoro (orig r7019): nickm | 2006-08-11 03:09:09 -0400 r7323@Kushana: nickm | 2006-08-10 22:04:57 -0700 Add sys/socket.h prereq for net/if.h and net/pfvar.h r7250@totoro (orig r7020): nickm | 2006-08-11 03:09:17 -0400 r7324@Kushana: nickm | 2006-08-10 23:23:15 -0700 Add more warnings to the list of those we tolerate. Start using GCC attributes more, for better error checking and better code generation. r7251@totoro (orig r7021): nickm | 2006-08-11 03:09:28 -0400 r7325@Kushana: nickm | 2006-08-10 23:37:31 -0700 Use gcc offsetof where available. r7252@totoro (orig r7022): nickm | 2006-08-11 03:09:35 -0400 r7326@Kushana: nickm | 2006-08-10 23:50:49 -0700 And another GCC change: predict that tor_frees() are usually real frees, and tor_asserts() usually wont happen. Other test should wait till -fprofile-arcs r7253@totoro (orig r7023): nickm | 2006-08-11 03:09:45 -0400 r7327@Kushana: nickm | 2006-08-11 00:00:36 -0700 Fix a pedantic warning r7254@totoro (orig r7024): nickm | 2006-08-11 03:09:52 -0400 r7328@Kushana: nickm | 2006-08-11 00:04:26 -0700 remove an assert that can never be false (an array in a non-null struct cannot be null). r7255@totoro (orig r7025): nickm | 2006-08-11 03:17:16 -0400 r7335@Kushana: nickm | 2006-08-11 00:13:03 -0700 fix wide lines r7256@totoro (orig r7026): arma | 2006-08-11 03:31:16 -0400 fix typo, add explanatory comment r7257@totoro (orig r7027): arma | 2006-08-11 03:41:21 -0400 fix funny-looking assignment that crashes unit tests r7258@totoro (orig r7028): nickm | 2006-08-11 03:42:11 -0400 r7337@Kushana: nickm | 2006-08-11 00:42:04 -0700 Only use __builtin_offsetof with gcc 4 or later r7259@totoro (orig r7029): nickm | 2006-08-11 03:51:34 -0400 r7341@Kushana: nickm | 2006-08-11 00:51:05 -0700 Amazing how much difference adding a ! to all your asserts can make. r7260@totoro (orig r7030): nickm | 2006-08-11 03:51:42 -0400 r7342@Kushana: nickm | 2006-08-11 00:51:25 -0700 Remove braindeadism. r7262@totoro (orig r7032): arma | 2006-08-11 20:30:07 -0400 more todo items r7263@totoro (orig r7033): arma | 2006-08-11 20:38:38 -0400 mark out the two biggest bugs r7264@totoro (orig r7034): arma | 2006-08-12 03:44:13 -0400 two more thoughts to consider for blocking resistance r7266@totoro (orig r7036): weasel | 2006-08-12 19:26:54 -0400 r8290@danube: weasel | 2006-08-13 01:26:01 +0200 Merge local r8289 into trunk: Update debian/copyright r7267@totoro (orig r7037): phobos | 2006-08-12 20:13:55 -0400 Updated details on how to check for universal binary. r7269@totoro (orig r7039): phobos | 2006-08-12 20:29:56 -0400 Apparently not everyone wants to build Universal binaries. Return valid Archictecture detection for inclusion into final dmg naming. r7272@totoro (orig r7042): arma | 2006-08-13 19:38:30 -0400 simplify the connection_write_to_buf_zlib() wrapper. r7273@totoro (orig r7043): arma | 2006-08-13 20:28:44 -0400 note three more bugs we should resolve r7274@totoro (orig r7044): arma | 2006-08-14 01:53:57 -0400 save 3-7 bytes per edge connection r7275@totoro (orig r7045): nickm | 2006-08-14 02:03:26 -0400 r7369@Kushana: nickm | 2006-08-14 02:03:10 -0400 Shortening fields is only one part of making structs shorter. You must also consider alignment padding. Whee. r7276@totoro (orig r7046): arma | 2006-08-14 02:27:39 -0400 two more todo's, plus a cleanup r7277@totoro (orig r7047): arma | 2006-08-14 02:29:40 -0400 this is what i really meant r7278@totoro (orig r7048): arma | 2006-08-14 03:08:29 -0400 simplify compare_cached_resolves_by_expiry() to make it match the idioms of other compare functions. hopefully i didn't break it? r7279@totoro (orig r7049): arma | 2006-08-14 04:55:41 -0400 fix typos and stuff r7280@totoro (orig r7050): arma | 2006-08-14 05:03:16 -0400 clarify that some old versions used an organizationName of "Tor", and others used "TOR". We should avoid both now. r7281@totoro (orig r7051): arma | 2006-08-14 05:04:27 -0400 rename HELLO cells to VERSIONS cells. r7282@totoro (orig r7052): arma | 2006-08-14 05:44:54 -0400 note a compile warning that we should investigate one day. r7283@totoro (orig r7053): arma | 2006-08-14 06:00:15 -0400 avoid complaining about our SOCKS proxy proxy. r7284@totoro (orig r7054): arma | 2006-08-14 06:16:42 -0400 checkpoint in-progress changelog notes r7287@totoro (orig r7057): nickm | 2006-08-14 16:16:21 -0400 r7383@Kushana: nickm | 2006-08-14 16:16:03 -0400 Stop walking entire dns cache for every request, now that we found our hash table bug. r7288@totoro (orig r7058): nickm | 2006-08-14 17:44:29 -0400 r7045@Kushana: nickm | 2006-08-05 13:56:44 -0400 mess with the TODO a little. r7289@totoro (orig r7059): nickm | 2006-08-14 17:44:34 -0400 r7046@Kushana: nickm | 2006-08-05 13:57:04 -0400 Make it possible for dns_init() to fail; note failure of eventdns configuratoin. r7290@totoro (orig r7060): nickm | 2006-08-14 17:44:39 -0400 r7291@totoro (orig r7061): nickm | 2006-08-14 17:44:45 -0400 r7386@Kushana: nickm | 2006-08-14 17:43:44 -0400 Patch from Adam Langley. * I meant getaddrinfo_a, not getaddrinfo_r - fixed * Added more checks to the parsing code. * It seems you switched an alloca to a malloc, but didn't add any frees r7292@totoro (orig r7062): nickm | 2006-08-14 23:54:09 -0400 r7392@Kushana: nickm | 2006-08-14 23:50:32 -0400 Only do the expensive version of router_have_minimum_dir_info() when the dir info has changed. Backport candidate, since oprofile suggests that this function and ones it calls account for 25-35% of oprofile samples. r7293@totoro (orig r7063): nickm | 2006-08-14 23:54:13 -0400 r7393@Kushana: nickm | 2006-08-14 23:51:07 -0400 remove extraneous #endif r7305@totoro (orig r7064): nickm | 2006-08-15 00:50:17 -0400 r7397@Kushana: nickm | 2006-08-15 00:46:18 -0400 Fix eventdns version of dns.c. Man, we need to get rid of this eventdns/dnsworker split. r7306@totoro (orig r7065): nickm | 2006-08-15 00:50:33 -0400 r7398@Kushana: nickm | 2006-08-15 00:49:50 -0400 Add missing backslash r7308@totoro (orig r7067): arma | 2006-08-15 22:18:55 -0400 dns.c:173: warning: control reaches end of non-void function r7309@totoro (orig r7068): arma | 2006-08-15 23:44:13 -0400 automatically avoid picking more than one node from the same /16 network when constructing a circuit. r7310@totoro (orig r7069): nickm | 2006-08-16 14:47:19 -0400 r7404@Kushana: nickm | 2006-08-16 09:32:19 -0400 Pass hints to getaddrinfo; fix bug 280 (?) r7311@totoro (orig r7070): nickm | 2006-08-16 14:47:24 -0400 r7405@Kushana: nickm | 2006-08-16 14:38:46 -0400 Implement reverse DNS lookup in eventdns: add new entry point; rename old entry point; revise TODO a little; add facility for parsing dns names. r7313@totoro (orig r7072): nickm | 2006-08-17 19:00:32 -0400 r7411@Kushana: nickm | 2006-08-17 19:00:25 -0400 patch suggested by Karsten Loesing: respond to SIGNAL command before we execute the signal, in case the signal shuts us down. r7314@totoro (orig r7073): arma | 2006-08-18 13:46:14 -0400 clean up AllowInvalidNodes man page entry. r7315@totoro (orig r7074): arma | 2006-08-18 14:19:35 -0400 fix typo pointed out by paul r8455@totoro (orig r8214): nickm | 2006-08-22 02:10:53 -0400 r8556@Kushana: nickm | 2006-08-22 01:22:46 -0400 Enable eventdns by default on platforms where we autoconf. This should be everything but windows. r8456@totoro (orig r8215): nickm | 2006-08-22 02:10:58 -0400 r8557@Kushana: nickm | 2006-08-22 02:10:12 -0400 dns-related TODO changes. r8462@totoro (orig r8221): arma | 2006-08-23 19:45:03 -0400 tor --verify-config now exits with -1(255) or 0 depending on whether the config options are bad or good. r8463@totoro (orig r8222): arma | 2006-08-23 20:54:18 -0400 clean up logging conventions in do_list_fingerprint() r8464@totoro (orig r8223): arma | 2006-08-24 00:51:55 -0400 make our socks5 handling more robust to broken socks clients: throw out everything waiting on the buffer in between socks handshake phases, since they can't possibly (so the theory goes) have predicted what we plan to respond to them. r8465@totoro (orig r8224): nickm | 2006-08-25 17:01:56 -0400 r8572@Kushana: nickm | 2006-08-25 16:35:49 -0400 Fix for bug 308: When we have a state file we cannot parse, tell the user, and move it aside. r8466@totoro (orig r8225): nickm | 2006-08-25 17:02:01 -0400 r8573@Kushana: nickm | 2006-08-25 16:55:19 -0400 Resolve bug 321 when using dnsworkers: append a period to every address we resolve at the exit node, so that we do not accidentally pick up local addresses, and so that failing searches are retried in the resolver search domains. (This is already solved for eventdns.) r8467@totoro (orig r8226): nickm | 2006-08-25 17:16:22 -0400 r8576@Kushana: nickm | 2006-08-25 17:16:01 -0400 Fix bug 314: Instead of checking address_in_virtualrange, check addressmap_have_mapping(). This should be more accurate. [Rename to addressmap_have_mapping() from addressmap_already_mapped().] r8468@totoro (orig r8227): arma | 2006-08-26 00:48:50 -0400 fix a log level -- err is for things that kill tor, warn is for things that tor can recover from. also, avoid situations where people who don't read their logs accumulate ten thousand useless files in their datadir. r8469@totoro (orig r8228): arma | 2006-08-26 00:52:22 -0400 more todo items when we avoid putting /16 servers into the same circuit r8470@totoro (orig r8229): arma | 2006-08-26 02:51:02 -0400 remove some more vestiges of cvs r8471@totoro (orig r8230): arma | 2006-08-26 02:56:16 -0400 complete an item, abandon an item, defer an item. r8472@totoro (orig r8231): arma | 2006-08-26 02:57:48 -0400 remove more completed items r8473@totoro (orig r8232): arma | 2006-08-26 03:13:54 -0400 make a 'real soon now' section of the todo, to point out what's more urgent r8474@totoro (orig r8233): arma | 2006-08-26 21:33:35 -0400 fix typo r8475@totoro (orig r8234): arma | 2006-08-26 21:41:08 -0400 i'm guessing nick meant to check the return value of dns_init. and revise some log severities to match the convention. r8476@totoro (orig r8235): arma | 2006-08-26 22:07:54 -0400 stop three memory leaks. nick, fix these if i'm wrong. r8477@totoro (orig r8236): arma | 2006-08-26 22:12:12 -0400 stop a big memory leak: we were leaking the whole contents of cached-routers.new every time we read it. r8478@totoro (orig r8237): arma | 2006-08-27 02:49:33 -0400 typo and whitespace r8479@totoro (orig r8238): arma | 2006-08-27 02:49:51 -0400 a usability improvement i just thought of r8480@totoro (orig r8239): arma | 2006-08-27 02:51:19 -0400 0.1.2.1-alpha will break blossom exit node functionality; put that on the todo list. r8481@totoro (orig r8240): arma | 2006-08-27 02:55:48 -0400 clean up the 0.1.2.1-alpha changelog r8482@totoro (orig r8241): arma | 2006-08-27 02:58:30 -0400 comment out an unused variable; nick, feel free to excise it further. r8483@totoro (orig r8242): arma | 2006-08-27 03:02:41 -0400 bump the doxyfile version number, since i made a doxygen ref man last week. r8484@totoro (orig r8243): arma | 2006-08-27 03:03:05 -0400 fix typo r8485@totoro (orig r8244): arma | 2006-08-27 03:03:17 -0400 bump to 0.1.2.1-alpha (does it work?) r8486@totoro (orig r8245): arma | 2006-08-27 03:26:00 -0400 turn eventdns off by default until we can get some more useful log messages into it. no use learning that it's broken without being able to learn what is broken too. r8488@totoro (orig r8247): phobos | 2006-08-27 20:40:11 -0400 Update rpms to require libevent 1.1b. r8490@totoro (orig r8249): phobos | 2006-08-27 21:06:44 -0400 Remove architecture from builds. The official builds are universal binaries. r8491@totoro (orig r8250): phobos | 2006-08-27 22:20:35 -0400 Replace nickm as packager of rpms. r8492@totoro (orig r8251): arma | 2006-08-27 23:01:07 -0400 bump trunk to 0.1.2.1-alpha-dev r8493@totoro (orig r8252): nickm | 2006-08-27 23:15:38 -0400 r8605@Kushana: nickm | 2006-08-27 14:01:11 -0400 divide eventdns log messages into warn and info messages. r8494@totoro (orig r8253): nickm | 2006-08-27 23:15:47 -0400 r8606@Kushana: nickm | 2006-08-27 14:04:19 -0400 Now that 0.1.2.1-alpha is out, make eventdns on-by-default again. (Hoop-lah.) r8495@totoro (orig r8254): nickm | 2006-08-27 23:15:50 -0400 r8607@Kushana: nickm | 2006-08-27 15:45:42 -0400 Change configuration strategy for eventdns. Instead of elaborate option set, just allow the user to specify another resolv.conf to use. r8496@totoro (orig r8255): nickm | 2006-08-27 23:15:55 -0400 r8608@Kushana: nickm | 2006-08-27 16:57:47 -0400 Make it possible to change nameserver options while Tor is running. r8497@totoro (orig r8256): nickm | 2006-08-27 23:16:02 -0400 r8609@Kushana: nickm | 2006-08-27 17:24:27 -0400 Add some doxygen, concentrated in dns.c r8499@totoro (orig r8258): nickm | 2006-08-27 23:29:51 -0400 Kill some SVK branches that I am done with. r8500@totoro (orig r8259): nickm | 2006-08-27 23:46:21 -0400 r8626@Kushana: nickm | 2006-08-27 23:45:46 -0400 Aw, crap. Non-gcc bug. We need regular windows builds. r8511@totoro (orig r8270): arma | 2006-08-28 04:24:36 -0400 put some symbols on the eventdns logging. leave the haphazard formatting alone. r8512@totoro (orig r8271): arma | 2006-08-28 14:51:36 -0400 fix bug found by Adam J. Richter: directory clients shouldn't hang up five minutes after they begin. but directory servers should still hang up after 5 minutes of failing to deliver any bytes. r8513@totoro (orig r8272): arma | 2006-08-28 15:00:17 -0400 an even better check -- now servers hang up if they can't write for a while, and clients hang up if they can't read for a while. r8514@totoro (orig r8273): arma | 2006-08-28 15:02:57 -0400 and make the comment better, while i'm at it r8518@totoro (orig r8277): nickm | 2006-08-28 16:42:14 -0400 r8652@Kushana: nickm | 2006-08-28 16:41:44 -0400 Resolve bug 324: strdup the right variable. r8519@totoro (orig r8278): nickm | 2006-08-28 16:50:47 -0400 r8654@Kushana: nickm | 2006-08-28 16:50:36 -0400 I bet real programmers even have programs that compile. r8521@totoro (orig r8280): nickm | 2006-08-29 00:22:51 -0400 r8657@Kushana: nickm | 2006-08-29 00:22:31 -0400 Mark an item complete; test new commit-email.pl script. r8539@totoro (orig r8298): weasel | 2006-08-29 16:43:52 -0400 r9565@galaxy: weasel | 2006-08-29 22:20:38 +0200 Do not reload or start when our config is known to be broken r8540@totoro (orig r8299): weasel | 2006-08-29 16:43:59 -0400 r9566@galaxy: weasel | 2006-08-29 22:32:26 +0200 Unnest all those nested if blocks r8541@totoro (orig r8300): weasel | 2006-08-29 16:44:05 -0400 r9567@galaxy: weasel | 2006-08-29 22:32:46 +0200 Prepare 0.1.2.1-alpha-1 r8542@totoro (orig r8301): weasel | 2006-08-29 16:44:11 -0400 r9568@galaxy: weasel | 2006-08-29 22:34:36 +0200 Upload target is experimental, not unstable r8543@totoro (orig r8302): weasel | 2006-08-29 16:44:17 -0400 r9569@galaxy: weasel | 2006-08-29 22:38:54 +0200 Change Standards-Version to 3.7.2. No changes required. r8544@totoro (orig r8303): nickm | 2006-08-29 17:59:20 -0400 r8682@Kushana: nickm | 2006-08-29 17:58:59 -0400 Fix compilation on GCC2 by disabling fun attributes unless __GNUC_MAJOR__ >= 3. r8548@totoro (orig r8307): nickm | 2006-08-30 19:34:49 -0400 r8686@Kushana: nickm | 2006-08-30 18:01:35 -0400 Remove bogus whitespace r8549@totoro (orig r8308): nickm | 2006-08-30 19:34:56 -0400 r8687@Kushana: nickm | 2006-08-30 19:33:28 -0400 Apply patch from Adam Langley: fix assert() in eventdns.c. [Fuzzing, apparently, is cool.] r8550@totoro (orig r8309): nickm | 2006-08-31 13:39:47 -0400 r8691@Kushana: nickm | 2006-08-31 13:30:46 -0400 Fix bug 327 (part 1): Use correct macro to test for GCC 3 or later. r8551@totoro (orig r8310): nickm | 2006-08-31 13:39:51 -0400 r8692@Kushana: nickm | 2006-08-31 13:38:07 -0400 Fix bug 327 (part 2): Cast char to unsigned char before passing to toupper/tolower. (Follow the same idiom as with isupper and friends, in case we run into the same problem on SGI or whereever it was.) r8552@totoro (orig r8311): nickm | 2006-08-31 14:46:46 -0400 r8695@Kushana: nickm | 2006-08-31 14:35:36 -0400 Fix two corner cases in router_dir_info_changed(). This should have no observable effect. r8553@totoro (orig r8312): nickm | 2006-08-31 14:47:54 -0400 r8696@Kushana: nickm | 2006-08-31 14:43:44 -0400 Try to appease some warnings with newer gccs that believe that ignoring a return value is okay, but casting a return value and then ignoring it is a sign of madness. r8556@totoro (orig r8315): phobos | 2006-08-31 19:52:41 -0400 Remove old TODO that I can't remember the details of it. r8557@totoro (orig r8316): nickm | 2006-09-02 01:33:11 -0400 r8704@Kushana: nickm | 2006-09-02 01:32:34 -0400 correct includes for net/if.h and net/pfvar.h on openbsd (from Tup) r8558@totoro (orig r8317): arma | 2006-09-02 19:26:42 -0400 Tor is more than code these days. r8559@totoro (orig r8318): nickm | 2006-09-02 22:13:52 -0400 r8708@Kushana: nickm | 2006-09-02 20:34:15 -0400 Fix warnings reported by weasel when compiling Tor on Debian woody. r8560@totoro (orig r8319): nickm | 2006-09-02 22:13:56 -0400 r8709@Kushana: nickm | 2006-09-02 20:59:54 -0400 Remove unused autoconf checks. r8561@totoro (orig r8320): weasel | 2006-09-03 16:19:35 -0400 r9648@danube: weasel | 2006-09-03 19:54:42 +0200 Document minor update of debian/copyright by arma in debian changelog r8562@totoro (orig r8321): arma | 2006-09-05 10:30:06 -0400 patch from tup r8563@totoro (orig r8322): chiussi | 2006-09-05 21:49:55 -0400 - made configure check if we are building for win32 - made configure link to required system dll's if building for win32 - added diffs for libevent 1.1b - forced user to turn off eventdns if win32 is set - cleaned up tor_mmap_file()_win32 (not sure if it's stable) - cleaned up some warnings and typos r8564@totoro (orig r8323): chiussi | 2006-09-05 21:58:55 -0400 - removed windows line endings r8565@totoro (orig r8324): weasel | 2006-09-05 22:08:52 -0400 r9665@danube: weasel | 2006-09-06 04:08:12 +0200 Remove svn:executable properties from all files in Win32Build r8566@totoro (orig r8325): nickm | 2006-09-06 04:42:12 -0400 r8723@Kushana: nickm | 2006-09-06 04:24:54 -0400 Clean up configure.in spaces, and make it work on Mac OS X again (for me). r8567@totoro (orig r8326): nickm | 2006-09-06 04:42:16 -0400 r8724@Kushana: nickm | 2006-09-06 04:32:28 -0400 Fix spaces; restore support for mapping files over 4GB on win32 (?) r8568@totoro (orig r8327): nickm | 2006-09-06 04:42:20 -0400 r8725@Kushana: nickm | 2006-09-06 04:39:29 -0400 spawn_func fixes: have cpuworker_main and dnsworker_main confirm to the right interfaces [casting func to void* is icky]. Also, make pthread_create() build without warnings. r8569@totoro (orig r8328): nickm | 2006-09-06 16:22:05 -0400 r8729@Kushana: nickm | 2006-09-06 16:20:40 -0400 Fix dns_cancel_pending_resolve() to realize pending resolves have expiry times, and should not be freed except when they fall off the pqueue. r8570@totoro (orig r8329): nickm | 2006-09-06 16:33:28 -0400 r8731@Kushana: nickm | 2006-09-06 16:33:19 -0400 Try to fix eventdns.c build on windows. r8571@totoro (orig r8330): nickm | 2006-09-06 17:35:30 -0400 r8733@Kushana: nickm | 2006-09-06 17:35:22 -0400 Add some missing defines to eventdns.c for windows. r8572@totoro (orig r8331): arma | 2006-09-06 20:30:29 -0400 new config option AvoidDiskWrites for people running tors on usb keys and other media that degrades when you write. not implemented yet, so just a reminder. r8573@totoro (orig r8332): chiussi | 2006-09-06 20:51:20 -0400 - fixed up typos in eventdns.c - configure lets user build with eventdns on win32 r8574@totoro (orig r8333): chiussi | 2006-09-06 20:54:28 -0400 rm'ed a tab (sorry) r8575@totoro (orig r8334): arma | 2006-09-06 21:00:37 -0400 prefer calling it a client rather than an OP r8576@totoro (orig r8335): arma | 2006-09-06 21:01:11 -0400 flesh out TODO re: AvoidDiskWrites r8577@totoro (orig r8336): arma | 2006-09-06 21:02:23 -0400 clean up and correct the spec r8578@totoro (orig r8337): arma | 2006-09-06 21:22:55 -0400 another controller event we left out r8579@totoro (orig r8338): arma | 2006-09-06 21:23:15 -0400 clean up some comments r8580@totoro (orig r8339): arma | 2006-09-06 23:26:17 -0400 help the rpm spec file recognize development versions now that they're not called foo-cvs r8581@totoro (orig r8340): arma | 2006-09-06 23:40:23 -0400 packages now start including svn website docs (oops) r8583@totoro (orig r8342): chiussi | 2006-09-06 23:53:14 -0400 - added some debugging lines to load_nameservers_from_registry() r8584@totoro (orig r8343): chiussi | 2006-09-06 23:58:46 -0400 - got rid of tabs (i think) r8585@totoro (orig r8344): chiussi | 2006-09-07 00:02:52 -0400 - got rid of tabs r8586@totoro (orig r8345): chiussi | 2006-09-07 02:34:20 -0400 fixed win32 eventdns snafu r8587@totoro (orig r8346): chiussi | 2006-09-07 02:36:22 -0400 undid whitespace changes to dns.c r8588@totoro (orig r8347): nickm | 2006-09-07 12:24:19 -0400 r8753@Kushana: nickm | 2006-09-07 12:23:25 -0400 Make eventdns spacing consistant r8589@totoro (orig r8348): nickm | 2006-09-07 12:32:06 -0400 r8755@Kushana: nickm | 2006-09-07 12:31:57 -0400 Tabify eventdns r8590@totoro (orig r8349): nickm | 2006-09-07 15:00:51 -0400 r8757@Kushana: nickm | 2006-09-07 13:07:46 -0400 Fix more compile warnings on Woody. r8591@totoro (orig r8350): chiussi | 2006-09-08 05:02:14 -0400 fixed typo in eventdns.c r8592@totoro (orig r8351): chiussi | 2006-09-08 05:05:07 -0400 another typo r8593@totoro (orig r8352): weasel | 2006-09-08 12:12:15 -0400 r9695@danube: weasel | 2006-09-08 18:11:53 +0200 Make tor build with -O0 r8594@totoro (orig r8353): arma | 2006-09-08 16:48:43 -0400 possibly make tor build and run on cygwin again. r8595@totoro (orig r8354): arma | 2006-09-08 23:18:39 -0400 add a "getinfo address" controller command. r8596@totoro (orig r8355): arma | 2006-09-08 23:38:03 -0400 fix encoding in "getinfo addr-mappings" response. fix error code when "getinfo dir/status/" fails. r8597@totoro (orig r8356): nickm | 2006-09-08 23:46:52 -0400 r8766@Kushana: nickm | 2006-09-08 23:46:12 -0400 Remove some completed items from TODO; defer a bunch of stuff from 0.1.2.x (which we want to have an RC for by the end of October) r8598@totoro (orig r8357): arma | 2006-09-09 15:16:07 -0400 fix typo, add log message r8599@totoro (orig r8358): arma | 2006-09-09 15:20:27 -0400 parameterize the loudness of get_interface_address() r8600@totoro (orig r8359): arma | 2006-09-09 15:36:51 -0400 start remembering X-Your-Address-Is hints even if you're a client, so you can become a server more smoothly. r8602@totoro (orig r8361): phobos | 2006-09-10 23:46:00 -0400 First attempt to document the process to build Win32 executables with mingw. Relies upon magic to actually work right now. r8611@totoro (orig r8370): nickm | 2006-09-11 22:50:14 -0400 r8776@Kushana: nickm | 2006-09-11 22:49:53 -0400 Avoid crash when telling controller stream-status and a stream is detached. Fixes bug 334. Backport candidate. r8612@totoro (orig r8371): phobos | 2006-09-11 22:58:33 -0400 Updates to win32-mingw. libevent1.1b builds. openssl doesn't. r8614@totoro (orig r8373): nickm | 2006-09-12 14:05:54 -0400 r8785@Kushana: nickm | 2006-09-12 14:05:46 -0400 Add non-dist utility for weasel r8615@totoro (orig r8374): weasel | 2006-09-12 14:11:19 -0400 r9734@danube: weasel | 2006-09-12 20:11:02 +0200 Set svn:keywords r8616@totoro (orig r8375): arma | 2006-09-12 14:31:03 -0400 avoid getting mail from ilja in 2008 about this. r8617@totoro (orig r8376): nickm | 2006-09-12 15:00:55 -0400 r8791@Kushana: nickm | 2006-09-12 15:00:48 -0400 As long as we are being pedantic, we may as well be extra-pedantic. r8624@totoro (orig r8383): nickm | 2006-09-13 13:34:59 -0400 r8795@Kushana: nickm | 2006-09-13 13:34:54 -0400 Try to split off the mingw portions of the libevent diff so we can send them to niels separately. Adding two patches: one that applies against 1.1b; one that applies against the libevent svn trunk. r8625@totoro (orig r8384): arma | 2006-09-13 18:24:05 -0400 two todo items we'll need for the blocking-resistance scheme r8626@totoro (orig r8385): arma | 2006-09-13 18:24:43 -0400 continue fleshing out the blocking-resistance design doc r8627@totoro (orig r8386): weasel | 2006-09-14 00:53:23 -0400 r9736@danube: weasel | 2006-09-14 05:53:06 +0200 Refactor dirserv_parse_fingerprint_file(fname) into dirserv_load_fingerprint_file(): There is not need to put together the path to the approved-routers file in more than one place. r8628@totoro (orig r8387): weasel | 2006-09-14 00:53:42 -0400 r9749@danube: weasel | 2006-09-14 06:53:12 +0200 Do not graciously increase the size to be mmaped if the current size already is at a page_size boundary. This is important since if a file has a size of zero and we mmap() it with length > 0, then accessing the mmaped memory area causes a bus error. However, if we pass a length of 0 to mmap() it will return with -1 and things work from there. r8629@totoro (orig r8388): nickm | 2006-09-14 01:00:02 -0400 r8808@senior-two-eighty: nickm | 2006-09-14 00:59:54 -0400 House style for no-args functions is old-style C, not C++ r8630@totoro (orig r8389): weasel | 2006-09-14 01:07:26 -0400 r9752@danube: weasel | 2006-09-14 07:06:49 +0200 Fix some dead code that is on occasion useful r8631@totoro (orig r8390): weasel | 2006-09-14 01:07:34 -0400 r9753@danube: weasel | 2006-09-14 07:07:02 +0200 Remove duplicates from MyFamily r8632@totoro (orig r8391): weasel | 2006-09-14 01:17:02 -0400 r9758@danube: weasel | 2006-09-14 07:10:12 +0200 Add a comment r8633@totoro (orig r8392): weasel | 2006-09-14 01:17:12 -0400 r9759@danube: weasel | 2006-09-14 07:14:37 +0200 Add smartlist_uniq() to TODO list r8634@totoro (orig r8393): arma | 2006-09-14 01:49:03 -0400 we should add a preamble to tor-design saying it's out of date. r8636@totoro (orig r8395): nickm | 2006-09-14 18:34:57 -0400 r8817@Kushana: nickm | 2006-09-14 18:31:29 -0400 Patch from steve hildrey: Generate network status correctly on non-versioning dirservers r8637@totoro (orig r8396): nickm | 2006-09-15 00:27:58 -0400 r8819@Kushana: nickm | 2006-09-15 00:27:45 -0400 Implement a smartlist_uniq() that will with luck not end the world. r8638@totoro (orig r8397): weasel | 2006-09-15 00:29:36 -0400 r9767@danube: weasel | 2006-09-15 06:27:48 +0200 Minor documentation fix r8639@totoro (orig r8398): weasel | 2006-09-15 01:20:16 -0400 r9770@danube: weasel | 2006-09-15 07:20:05 +0200 router_set_networkstatus() gets a list of status documents we asked for from connection_dir_client_reached_eof(). However, as a cache we (sometimes?) just ask for "all". router_set_networkstatus() would freak out over that, meaning it would log a warning and drop the status document instead of caching it as it is supposed to. Now we let router_set_networkstatus() know if the data comes from an all-request so it can do the right thing. r8640@totoro (orig r8399): arma | 2006-09-15 01:30:25 -0400 Send out a burst of long-range drop cells after we've established that we're reachable. Spread them over 4 circuits, so hopefully a few will be fast. This exercises our bandwidth and bootstraps us quicker. r8641@totoro (orig r8400): arma | 2006-09-15 01:53:00 -0400 make my bandwidth exercises actually happen r8642@totoro (orig r8401): arma | 2006-09-15 02:06:09 -0400 two todo items r8643@totoro (orig r8402): arma | 2006-09-15 14:03:53 -0400 avoid thrashing the bandwidth exercise when we change IPs a lot. (we avoid simply by not doing any new tests when we change IPs -- it looks like we retain our previous bandwidth estimates, so there's no need to do new exercise. though in some cases new exercises may still be useful. one day we'll do something smarter.) r8644@totoro (orig r8403): arma | 2006-09-15 14:07:11 -0400 actually, do the bandwidth test anyway, if you've been up at least 6 hours at your previous address. r8645@totoro (orig r8404): nickm | 2006-09-15 16:19:55 -0400 r8821@Kushana: nickm | 2006-09-15 16:19:16 -0400 Clean up libevent difmingw f problems noticed by mikec. r8647@totoro (orig r8406): phobos | 2006-09-16 15:54:03 -0400 Re-create symlinks through cp. r8648@totoro (orig r8407): arma | 2006-09-17 02:18:06 -0400 build testing circuits more rapidly. this has a failure mode where if circuits fail quickly, we'll fail a lot of them very quickly and not retry for a while. so be it. r8649@totoro (orig r8408): arma | 2006-09-17 13:57:56 -0400 a few more todo changes r8650@totoro (orig r8409): nickm | 2006-09-17 15:58:24 -0400 r8837@Kushana: nickm | 2006-09-17 15:58:04 -0400 More TODO work; tenatively mark assignments. r8651@totoro (orig r8410): nickm | 2006-09-17 16:12:10 -0400 r8839@Kushana: nickm | 2006-09-17 16:11:59 -0400 Add some client performance XXXXs; try to move some common case tests higher on their decision trees. r8652@totoro (orig r8411): nickm | 2006-09-17 16:20:23 -0400 r8841@Kushana: nickm | 2006-09-17 16:20:16 -0400 Move more todo items around r8653@totoro (orig r8412): arma | 2006-09-18 00:24:41 -0400 Fix two bugs: first, "extendcircuit" would crash if you gave it a purpose. Second, if you give an unknown purpose, it would say: 552 Unknown purpose "purpose=foo" Now it just says 552 Unknown purpose "foo" r8656@totoro (orig r8415): arma | 2006-09-18 00:59:15 -0400 good god, that was a bad idea. i've built 500 circuits in the past 5 minutes, trying to establish reachability of my unreachable server. r8657@totoro (orig r8416): phobos | 2006-09-18 01:35:08 -0400 Fix a mis-spelled Privoxy. r8668@totoro (orig r8427): nickm | 2006-09-19 16:41:31 -0400 Merge in some bsockets calls, all wrapped inside #if defined(USE_BSOCKETS) r8669@totoro (orig r8428): nickm | 2006-09-19 16:45:20 -0400 Fix a bogus free() in eventdns.c. Bug reported by xiando. r8670@totoro (orig r8429): arma | 2006-09-19 17:37:03 -0400 eventdns patch from Adam Langley r8671@totoro (orig r8430): nickm | 2006-09-19 18:20:09 -0400 Switch routerlist.c to using memcmp on digests rather than crypto_pk_cmp_keys(); speed up find_whitespace a lot (8x for me) by using a switch statement. This should speed parsing a lot of routers at once by a lot. r8672@totoro (orig r8431): nickm | 2006-09-19 18:36:48 -0400 Malloc and friends are critical-path: Thus, add an it-wont-happen branch prediction for NULL returns, and skip the malloc(0) check on platforms where malloc(0) returns a pointer. r8673@totoro (orig r8432): nickm | 2006-09-19 19:18:30 -0400 Stop searching routerlist for routers with the same identity as other routers (on router insert): we already have a map for that. (We need to add an index field to routerinfo_t so we can figure out which point in the routerlist to replace.) Also, add a comment to routerlist.c; arma, please advise? r8674@totoro (orig r8433): nickm | 2006-09-19 19:48:14 -0400 NEEDS REVIEW. Act on previous comment, and handle named servers differently: now, we allow multiple servers with the same name in the routerlist even if that name is reserved, but we check whether names are reserved when we try to look up routers by nickname. This is a minor security fix. This makes router_add_to_routerlist O(1). This is a backport candidate. r8675@totoro (orig r8434): nickm | 2006-09-19 19:55:35 -0400 Speed up eat_whitespace by a lot. r8678@totoro (orig r8437): nickm | 2006-09-21 17:48:06 -0400 r8872@Kushana: nickm | 2006-09-21 14:00:20 -0400 Implement server-side reverse DNS using eventdns. Add an option to routerdescs so we can tell which servers have eventdns enabled. r8679@totoro (orig r8438): nickm | 2006-09-21 17:48:11 -0400 r8873@Kushana: nickm | 2006-09-21 14:38:22 -0400 Fix a bug: Remember, each call to escaped() replaces the value returned from the last call to escaped(). r8680@totoro (orig r8439): nickm | 2006-09-21 17:48:16 -0400 r8874@Kushana: nickm | 2006-09-21 15:22:27 -0400 Rename and document SearchDomains and ResolvConf options; warn if ServerDNSResolvConfFile is given but eventdns isnt enabled. r8681@totoro (orig r8440): nickm | 2006-09-21 17:48:22 -0400 r8875@Kushana: nickm | 2006-09-21 16:46:28 -0400 Resolve bug 330: detect ISPs that want to hijack failing DNS requests and basically domain-squat the entire internet. r8682@totoro (orig r8441): nickm | 2006-09-21 17:48:37 -0400 r8876@Kushana: nickm | 2006-09-21 16:58:46 -0400 Trivial cleanup: !!x is a weird way to spell x != NULL. r8683@totoro (orig r8442): nickm | 2006-09-21 17:48:42 -0400 r8877@Kushana: nickm | 2006-09-21 17:12:33 -0400 Consider non-exit servers unsuitable for RESOLVE commands. r8684@totoro (orig r8443): nickm | 2006-09-21 17:48:55 -0400 r8878@Kushana: nickm | 2006-09-21 17:15:47 -0400 Trivial whitespace cleanups. r8685@totoro (orig r8444): nickm | 2006-09-21 17:49:03 -0400 r8879@Kushana: nickm | 2006-09-21 17:20:31 -0400 Oops from earlier patch; add "opt eventdns" to unittests, fix bug in routerdesc generation. r8686@totoro (orig r8445): nickm | 2006-09-21 17:49:15 -0400 r8880@Kushana: nickm | 2006-09-21 17:26:02 -0400 Trivial patch to appease warn-happy gcc: mark unused parameter r8687@totoro (orig r8446): nickm | 2006-09-21 17:49:36 -0400 r8881@Kushana: nickm | 2006-09-21 17:27:59 -0400 Allow resolve requests to non-exits when they are specifically requested (via resolve foo.bar.exit). r8688@totoro (orig r8447): nickm | 2006-09-21 18:24:45 -0400 Fix bug in r8440: base32 uses 5 bytes per char, not 4. r8689@totoro (orig r8448): nickm | 2006-09-21 18:57:07 -0400 Fix stupid C mistake. Glad I caught that one fast. r8690@totoro (orig r8449): nickm | 2006-09-21 20:10:26 -0400 Fix a couple of server-side reverse dns bugs r8691@totoro (orig r8450): arma | 2006-09-21 20:24:27 -0400 remove some loud log messages r8692@totoro (orig r8451): nickm | 2006-09-21 20:43:55 -0400 r8894@Kushana: nickm | 2006-09-21 18:30:42 -0400 Specify and implement SOCKS5 interface for reverse hostname lookup. r8693@totoro (orig r8452): nickm | 2006-09-21 20:44:07 -0400 r8895@Kushana: nickm | 2006-09-21 20:05:11 -0400 Debug client-side reverse dns code. r8694@totoro (orig r8453): nickm | 2006-09-21 20:44:21 -0400 r8695@totoro (orig r8454): nickm | 2006-09-21 20:45:08 -0400 r8901@Kushana: nickm | 2006-09-21 20:43:48 -0400 I tracked a bug in server-side reverse DNS to something concerning the caching code. Ive disabled server-side cacheing for reverse DNS answers for now, and I am noting the bug in the TODO. r8696@totoro (orig r8455): phobos | 2006-09-21 21:15:07 -0400 Removed verbose patch output. r8697@totoro (orig r8456): nickm | 2006-09-21 21:23:28 -0400 r8906@Kushana: nickm | 2006-09-21 21:23:22 -0400 Revise patch for libevent 1.1b to handle the "wait, I *do* have a gettimeofday()" case. r8698@totoro (orig r8457): phobos | 2006-09-22 11:08:05 -0400 Finally, valid tor.exe directions from scratch with mingw. r8699@totoro (orig r8458): phobos | 2006-09-22 11:37:52 -0400 One last change to openssl to get it to compile in mingw. r8702@totoro (orig r8461): arma | 2006-09-22 15:29:26 -0400 remove 8888 as a long lived port. i can't remember why it's on the list. r8703@totoro (orig r8462): nickm | 2006-09-22 16:18:58 -0400 r8843@Kushana: nickm | 2006-09-17 16:57:20 -0400 A couple of last minor TODO cleanup items r8704@totoro (orig r8463): nickm | 2006-09-22 16:19:34 -0400 r8912@Kushana: nickm | 2006-09-22 16:18:51 -0400 Write more of path-spec.txt r8705@totoro (orig r8464): nickm | 2006-09-22 16:20:21 -0400 r8909@Kushana: nickm | 2006-09-22 00:38:13 -0400 Consider changes to has_old_dnsworkers as noncosmetic r8706@totoro (orig r8465): nickm | 2006-09-22 16:20:26 -0400 r8910@Kushana: nickm | 2006-09-22 12:14:05 -0400 Instead of just checking known-invalid addresses for DNS hijacking, we now check randomly generated addresses, and if too many of them map to the same IP, we assume that IP is the destination of a DNS hijack attempt. A little bird tells me that some DNS hijackers think that declining to give an A record for RFC2606 addresses (like .invalid and .example) makes them more standards compliant. Standardswise, this is like an illicit brothel making sure that nobody has pulled the tags off the mattresss, but that doesn't get us out of working around it. r8707@totoro (orig r8466): nickm | 2006-09-22 16:20:35 -0400 r8911@Kushana: nickm | 2006-09-22 12:24:52 -0400 Make exitlist only output each result line once. r8708@totoro (orig r8467): phobos | 2006-09-22 22:18:18 -0400 Special mingw package_nsis and nsi scripts. r8713@totoro (orig r8472): phobos | 2006-09-23 15:17:08 -0400 Changes to doc as a result of creating a MinGW tor.exe on a bare system. Add two TODO items relating to MinGW instructions. svn:r8473
-rw-r--r--ChangeLog171
-rw-r--r--Doxyfile2
-rw-r--r--Makefile.am11
-rw-r--r--Win32Build/mingw/CHANGES-libevent80
-rw-r--r--Win32Build/mingw/README8
-rw-r--r--Win32Build/mingw/libevent-1.1b-mingw-custom.diff338
-rw-r--r--Win32Build/mingw/libevent-1.1b-mingw.diff221
-rw-r--r--Win32Build/mingw/libevent-svn-mingw.diff210
-rw-r--r--[-rwxr-xr-x]Win32Build/vc6/Tor.dsw0
-rw-r--r--[-rwxr-xr-x]Win32Build/vc6/tor/Tor.dsp8
-rw-r--r--[-rwxr-xr-x]Win32Build/vc6/tor_resolve/tor_resolve.dsp0
-rw-r--r--[-rwxr-xr-x]Win32Build/vc7/Tor/Tor.sln0
-rw-r--r--[-rwxr-xr-x]Win32Build/vc7/Tor/Tor.vcproj0
-rw-r--r--[-rwxr-xr-x]Win32Build/vc7/tor_resolve/tor_resolve.vcproj0
-rw-r--r--[-rwxr-xr-x]Win32Build/vc7/unittests/unittests.vcproj0
-rw-r--r--configure.in212
-rw-r--r--contrib/Makefile.am2
-rwxr-xr-xcontrib/add-tor115
-rwxr-xr-xcontrib/checkSpace.pl75
-rw-r--r--contrib/cross.sh150
-rwxr-xr-xcontrib/exitlist20
-rw-r--r--contrib/id_to_fp.c64
-rw-r--r--contrib/osx/Makefile.am3
-rw-r--r--contrib/osx/TorPostflight27
-rw-r--r--contrib/osx/TorPreFlight29
-rw-r--r--contrib/osx/Tor_Uninstaller.app.tar.gzbin0 -> 20172 bytes
-rw-r--r--contrib/osx/Tor_Uninstaller.applescript57
-rw-r--r--contrib/osx/package.sh3
-rwxr-xr-xcontrib/osx/uninstall_tor_bundle.sh2
-rw-r--r--contrib/package_nsis-mingw.sh59
-rw-r--r--contrib/tor-mingw.nsi.in212
-rwxr-xr-xcontrib/tor-resolve.py75
-rw-r--r--contrib/tor.nsi.in2
-rwxr-xr-xcontrib/torify.in42
-rw-r--r--debian/README.Debian7
-rw-r--r--debian/changelog23
-rw-r--r--debian/control2
-rw-r--r--debian/copyright2
-rw-r--r--debian/patches/00list1
-rwxr-xr-xdebian/patches/06_add_compile_time_defaults.dpatch124
-rwxr-xr-xdebian/patches/07_log_to_file_by_default.dpatch19
-rwxr-xr-xdebian/patches/11_tor_as_root_more_helpful.dpatch36
-rwxr-xr-xdebian/rules7
-rw-r--r--debian/tor.init92
-rw-r--r--doc/HACKING25
-rw-r--r--doc/TODO512
-rw-r--r--doc/control-spec-v0.txt5
-rw-r--r--doc/control-spec.txt24
-rw-r--r--doc/design-paper/blocking.tex352
-rw-r--r--doc/dir-spec-v1.txt315
-rw-r--r--doc/dir-spec.txt233
-rw-r--r--doc/incentives.txt2
-rw-r--r--doc/path-spec.txt551
-rw-r--r--doc/socks-extensions.txt6
-rw-r--r--doc/tor-osx-dmg-creation.txt3
-rw-r--r--doc/tor-rpm-creation.txt27
-rw-r--r--doc/tor-spec-udp.txt27
-rw-r--r--doc/tor-spec-v0.txt734
-rw-r--r--doc/tor-spec.txt580
-rw-r--r--doc/tor-win32-mingw-creation.txt120
-rw-r--r--doc/tor.1.in81
-rw-r--r--src/common/compat.c201
-rw-r--r--src/common/compat.h123
-rw-r--r--src/common/container.c174
-rw-r--r--src/common/container.h42
-rw-r--r--src/common/crypto.c74
-rw-r--r--src/common/crypto.h2
-rw-r--r--src/common/ht.h101
-rw-r--r--src/common/log.c9
-rw-r--r--src/common/log.h12
-rw-r--r--src/common/torgzip.c96
-rw-r--r--src/common/torgzip.h12
-rw-r--r--src/common/torint.h6
-rw-r--r--src/common/tortls.c159
-rw-r--r--src/common/tortls.h4
-rw-r--r--src/common/util.c163
-rw-r--r--src/common/util.h87
-rw-r--r--src/config/torrc.sample.in51
-rw-r--r--src/or/Makefile.am10
-rw-r--r--src/or/buffers.c114
-rw-r--r--src/or/circuitbuild.c293
-rw-r--r--src/or/circuitlist.c589
-rw-r--r--src/or/circuituse.c417
-rw-r--r--src/or/command.c111
-rw-r--r--src/or/config.c516
-rw-r--r--src/or/connection.c536
-rw-r--r--src/or/connection_edge.c684
-rw-r--r--src/or/connection_or.c286
-rw-r--r--src/or/control.c472
-rw-r--r--src/or/cpuworker.c27
-rw-r--r--src/or/directory.c612
-rw-r--r--src/or/dirserv.c497
-rw-r--r--src/or/dns.c1308
-rw-r--r--src/or/eventdns.c2292
-rw-r--r--src/or/eventdns.h73
-rw-r--r--src/or/eventdns_tor.h13
-rw-r--r--src/or/hibernate.c20
-rw-r--r--src/or/main.c328
-rw-r--r--src/or/onion.c12
-rw-r--r--src/or/or.h837
-rw-r--r--src/or/policies.c38
-rw-r--r--src/or/relay.c333
-rw-r--r--src/or/rendclient.c108
-rw-r--r--src/or/rendcommon.c27
-rw-r--r--src/or/rendmid.c84
-rw-r--r--src/or/rendservice.c109
-rw-r--r--src/or/rephist.c91
-rw-r--r--src/or/router.c308
-rw-r--r--src/or/routerlist.c452
-rw-r--r--src/or/routerparse.c102
-rw-r--r--src/or/test.c157
-rw-r--r--src/tools/tor-resolve.c2
-rw-r--r--src/win32/orconfig.h2
-rw-r--r--tor.spec.in6
114 files changed, 14587 insertions, 4703 deletions
diff --git a/ChangeLog b/ChangeLog
index 8fe89f007e..6912bfb788 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,22 +1,164 @@
-Changes in version 0.1.1.24 - 2006-09-xx [ongoing]
- o Major bugfixes:
- - Allow really slow clients to not hang up five minutes into their
- directory downloads (suggested by Adam J. Richter).
- - Fix major performance regression from 0.1.0.x: instead of checking
- whether we have enough directory information every time we want to
- do something, only check when the directory information has changed.
- This should improve client CPU usage by 25-50%.
+Changes in version 0.1.2.2-alpha - 2006-??-??
+ o Major features:
+ - Add server-side support for "reverse" DNS lookups (using PTR
+ records so clients can determine the canonical hostname for a given
+ IPv4 address). This has been specified for a long time, but was
+ previously never implemented. This is only supported by eventdns;
+ servers now announce in their descriptors whether they support
+ eventdns.
+ - Specify and implement client-side SOCKS5 interface for reverse DNS
+ lookups; see doc/socks-extensions.txt for full information.
+
+ o Minor features:
+ - Check for name servers (like Earthlink's) that hijack failing DNS
+ requests and replace the 'no such server' answer with a "helpful"
+ redirect to an advertising-driven search portal. We're a little clever
+ about this, in order to work around DNS hijackers who "helpfully"
+ decline to hijack known-invalid RFC2606 addresses. [Resolves bug 330.]
+ - When asked to resolve a hostname, don't use non-exit servers unless
+ requested to do so. This allows servers with broken DNS be useful to
+ the network.
+
+ o Security Fixes, minor
+ - If a client asked for a server by name, and we didn't have a
+ descriptor for a named server with that name, we might return an old
+ one.
+
+ o Minor Bugfixes
+ - Small performance improvements on parsing descriptors (x2).
+ - Major performance descriptor on inserting descriptors; change
+ algorithm from O(n^2) to O(n).
+ - Make the common memory allocation path faster on machines where
+ malloc(0) returns a pointer.
+ - Fix a debug log message in eventdns to say "X resolved to Y"
+ instead of "X resolved to X".
+ - Prevent the 'exitlist' script from printing the same result more
+ than once.
+
+ o Documentation
+ - Documented (and renamed) ServerDNSSearchDomains and
+ ServerDNSResolvConfFile options.
+
+Changes in version 0.1.2.1-alpha - 2006-08-27
+ o Major features:
+ - Add "eventdns" async dns library from Adam Langley, tweaked to
+ build on OSX and Windows. Only enabled if you pass the
+ --enable-eventdns argument to configure.
+ - Allow servers with no hostname or IP address to learn their
+ IP address by asking the directory authorities. This code only
+ kicks in when you would normally have exited with a "no address"
+ error. Nothing's authenticated, so use with care.
+ - Rather than waiting a fixed amount of time between retrying
+ application connections, we wait only 5 seconds for the first,
+ 10 seconds for the second, and 15 seconds for each retry after
+ that. Hopefully this will improve the expected user experience.
+ - Patch from Tup to add support for transparent AP connections:
+ this basically bundles the functionality of trans-proxy-tor
+ into the Tor mainline. Now hosts with compliant pf/netfilter
+ implementations can redirect TCP connections straight to Tor
+ without diverting through SOCKS. Needs docs.
+ - Busy directory servers save lots of memory by spooling server
+ descriptors, v1 directories, and v2 networkstatus docs to buffers
+ as needed rather than en masse. Also mmap the cached-routers
+ files, so we don't need to keep the whole thing in memory too.
+ - Automatically avoid picking more than one node from the same
+ /16 network when constructing a circuit.
+ - Revise and clean up the torrc.sample that we ship with; add
+ a section for BandwidthRate and BandwidthBurst.
+
+ o Minor features:
+ - Split circuit_t into origin_circuit_t and or_circuit_t, and
+ split connection_t into edge, or, dir, control, and base structs.
+ These will save quite a bit of memory on busy servers, and they'll
+ also help us track down bugs in the code and bugs in the spec.
+ - Experimentally re-enable kqueue on OSX when using libevent 1.1b
+ or later. Log when we are doing this, so we can diagnose it when
+ it fails. (Also, recommend libevent 1.1b for kqueue and
+ win32 methods; deprecate libevent 1.0b harder; make libevent
+ recommendation system saner.)
+ - Start being able to build universal binaries on OS X (thanks
+ to Phobos).
+ - Export the default exit policy via the control port, so controllers
+ don't need to guess what it is / will be later.
+ - Add a man page entry for ProtocolWarnings.
+ - Add TestVia config option to the man page.
+ - Remove even more protocol-related warnings from Tor server logs,
+ such as bad TLS handshakes and malformed begin cells.
+ - Stop fetching descriptors if you're not a dir mirror and you
+ haven't tried to establish any circuits lately. [This currently
+ causes some dangerous behavior, because when you start up again
+ you'll use your ancient server descriptors.]
+ - New DirPort behavior: if you have your dirport set, you download
+ descriptors aggressively like a directory mirror, whether or not
+ your ORPort is set.
+ - Get rid of the router_retry_connections notion. Now routers
+ no longer try to rebuild long-term connections to directory
+ authorities, and directory authorities no longer try to rebuild
+ long-term connections to all servers. We still don't hang up
+ connections in these two cases though -- we need to look at it
+ more carefully to avoid flapping, and we likely need to wait til
+ 0.1.1.x is obsolete.
+ - Drop compatibility with obsolete Tors that permit create cells
+ to have the wrong circ_id_type.
+ - Re-enable per-connection rate limiting. Get rid of the "OP
+ bandwidth" concept. Lay groundwork for "bandwidth classes" --
+ separate global buckets that apply depending on what sort of conn
+ it is.
+ - Start publishing one minute or so after we find our ORPort
+ to be reachable. This will help reduce the number of descriptors
+ we have for ourselves floating around, since it's quite likely
+ other things (e.g. DirPort) will change during that minute too.
+ - Fork the v1 directory protocol into its own spec document,
+ and mark dir-spec.txt as the currently correct (v2) spec.
- o Minor bugfixes:
+ o Major bugfixes:
+ - When we find our DirPort to be reachable, publish a new descriptor
+ so we'll tell the world (reported by pnx).
+ - Publish a new descriptor after we hup/reload. This is important
+ if our config has changed such that we'll want to start advertising
+ our DirPort now, etc.
- Allow Tor to start when RunAsDaemon is set but no logs are set.
- - Don't crash when the controller receives a third argument to an
- "extendcircuit" request.
+ - When we have a state file we cannot parse, tell the user and
+ move it aside. Now we avoid situations where the user starts
+ Tor in 1904, Tor writes a state file with that timestamp in it,
+ the user fixes her clock, and Tor refuses to start.
- Fix configure.in to not produce broken configure files with
more recent versions of autoconf. Thanks to Clint for his auto*
voodoo.
- - Fix NetBSD bug that could allow someone to force uninitialized RAM
- to be sent to a server's DNS resolver. This only affects NetBSD and
- other platforms that do not bounds-check tolower().
+ - "tor --verify-config" now exits with -1(255) or 0 depending on
+ whether the config options are bad or good.
+ - Resolve bug 321 when using dnsworkers: append a period to every
+ address we resolve at the exit node, so that we do not accidentally
+ pick up local addresses, and so that failing searches are retried
+ in the resolver search domains. (This is already solved for
+ eventdns.) (This breaks Blossom servers for now.)
+ - If we are using an exit enclave and we can't connect, e.g. because
+ its webserver is misconfigured to not listen on localhost, then
+ back off and try connecting from somewhere else before we fail.
+
+ o Minor bugfixes:
+ - Start compiling on MinGW on Windows (patches from Mike Chiussi).
+ - Start compiling on MSVC6 on Windows (patches from Frediano Ziglio).
+ - Fix bug 314: Tor clients issued "unsafe socks" warnings even
+ when the IP address is mapped through MapAddress to a hostname.
+ - Start passing "ipv4" hints to getaddrinfo(), so servers don't do
+ useless IPv6 DNS resolves.
+ - Patch suggested by Karsten Loesing: respond to SIGNAL command
+ before we execute the signal, in case the signal shuts us down.
+ - Clean up AllowInvalidNodes man page entry.
+ - Claim a commonname of Tor, rather than TOR, in TLS handshakes.
+ - Add more asserts to track down an assert error on a windows Tor
+ server with connection_add being called with socket == -1.
+ - Handle reporting OR_CONN_EVENT_NEW events to the controller.
+ - Fix misleading log messages: an entry guard that is "unlisted",
+ as well as not known to be "down" (because we've never heard
+ of it), is not therefore "up".
+ - Remove code to special-case "-cvs" ending, since it has not
+ actually mattered since 0.0.9.
+ - Make our socks5 handling more robust to broken socks clients:
+ throw out everything waiting on the buffer in between socks
+ handshake phases, since they can't possibly (so the theory
+ goes) have predicted what we plan to respond to them.
Changes in version 0.1.1.23 - 2006-07-30
@@ -57,7 +199,6 @@ Changes in version 0.1.1.22 - 2006-07-05
- Fix a rare bug that was causing some servers to complain about
"closing wedged cpuworkers" and skip some circuit create requests.
- Make the Exit flag in directory status documents actually work.
- - Add a preliminary OSX binary un-installer, and fix permissions.
Changes in version 0.1.1.21 - 2006-06-10
diff --git a/Doxyfile b/Doxyfile
index e0726bb768..9cde9d6e13 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -25,7 +25,7 @@ PROJECT_NAME = tor
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 0.1.1.16-rc
+PROJECT_NUMBER = 0.1.2.1-alpha
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
diff --git a/Makefile.am b/Makefile.am
index 76be9982ca..6179291fd1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,7 @@ EXTRA_DIST = INSTALL README AUTHORS LICENSE ChangeLog tor.spec tor.spec.in
#install-data-local:
# $(INSTALL) -m 755 -d $(LOCALSTATEDIR)/lib/tor
+# Assume a tarball is in .. for now.
dist-rpm:
RPM_BUILD_DIR="/tmp/tor-rpm-build-$$$$"; \
rm -rf $$RPM_BUILD_DIR; \
@@ -22,7 +23,7 @@ dist-rpm:
for subdir in BUILD RPMS SOURCES SPECS SRPMS; do \
mkdir $$RPM_BUILD_DIR/$$subdir; \
done; \
- mkdir $$RPM_BUILD_DIR/SOURCES/tor-$(VERSION); \
+ mkdir $$RPM_BUILD_DIR/SOURCES/tor-$(VERSION); \
cp -R ./ $$RPM_BUILD_DIR/SOURCES/tor-$(VERSION)/; \
pushd $$RPM_BUILD_DIR/SOURCES/; \
tar zcf tor-$(VERSION).tar.gz ./; \
@@ -54,8 +55,8 @@ doxygen:
# Avoid strlcpy.c, strlcat.c, tree.h
check-spaces:
- ./contrib/checkSpace.pl -C \
- src/common/*.h \
- src/common/[^s]*.c \
- src/or/[^t]*.[ch] src/or/t*.c
+ ./contrib/checkSpace.pl -C \
+ src/common/*.h \
+ src/common/[^as]*.c \
+ src/or/[^et]*.[ch] src/or/t*.c src/or/eventdns_tor.h
diff --git a/Win32Build/mingw/CHANGES-libevent b/Win32Build/mingw/CHANGES-libevent
new file mode 100644
index 0000000000..e723d9755f
--- /dev/null
+++ b/Win32Build/mingw/CHANGES-libevent
@@ -0,0 +1,80 @@
+Changes related to compilation under MinGW/any sane win32 gcc
+=============================================================
+
+* event.c
+- If gcc include "WIN32-Code/misc.h" instead of "misc.h"
+
+* WIN32-Code/misc.h
+- Add struct prototypes for timeval and timezone
+
+* buffer.c
+- changed type of "i" from "u_int" to "unsigned int". My MinGW wasn't
+ recognizing it. (u_int is normally typedef'ed to unsigned int, right?)
+
+* evbuffer.c
+- removed incorrect win32 error checking, see bufferevent_writecb().
+ (this needs to be fixed by anyone planning to use evbuffer on win32)
+
+* log.c
+- If gcc include "WIN32-Code/misc.h" instead of "misc.h"
+
+* WIN32-Code/misc.c
+- if gcc, include "misc.h"
+- added newline at end of file to shut up gcc
+
+* WIN32-Code/win32.c
+- Altered the prototypes of win32_*() so their argument types didn't conflict
+ with the function definitions.
+- Casted types of win32_* to void inside win32ops so that it didn't conflict
+ with the definition of eventops (gcc doesn't like this)
+- Altered prototype of signal_handler to be static since definition is static
+ (why wasn't it like this before)
+- Casted the second argument of signal() to be void*, some reason my MinGW
+ doesn't have sighandler_t typedef'ed.
+
+* configure.in
+- some code to check if we are compiling for WIN32.
+
+* Makefile.am
+- if BUILD_WIN32 is defined, include WIN32-Code/misc.c and
+ WIN32-Code/win32.c as source files.
+- if WIN32, do not build test stuff. (not windows friendly)
+- if WIN32, explicitly link to ws2_32.dll
+
+Notes
+-----
+- We assume that if __GNUC__ is undefined we are building with MSVC
+- If the user wishes to build a dll, they are on their own, the syntax is
+ compiler specific.
+- Getting this warning from libtool, no idea why
+ "libtool: link: warning: undefined symbols not allowed in i686-pc-mingw32
+ shared libraries"
+
+
+Changes related to "custom eventops"
+====================================
+
+* configure.in
+- add argument --enable-custom-eventops, sets USE_CUSTOM_EVENTOPS in config.h
+- add argument --enable-custom-code, sets USE_CUSTOM_CODE in Makefile
+
+* Makefile.am
+- if USE_CUSTOM_CODE, include custom/custom.c as a source file.
+ (I can't think of a way to pass a string to Makefile.am, so I'm stuck naming
+ the new source file custom.c. It just seems simpler this way, but I'm open
+ to suggestions)
+
+* event.c
+- if USE_CUSTOM_EVENTOPS, use eventops as defined in custom-eventops.h
+
+Notes
+-----
+Just in case it isn't completely obvious, the goal of "custom eventops" is to
+allow the user to include their own event processing system without requiring a
+fork. This is accomplished through two parts. Firstly, by allowing the user to
+redefine eventops. (for example, the user may wish to use epoll() exclusively).
+Secondly, by allowing the user to include their own code to support a private
+eventop (note, this may not be necessary, as the user may choose to include
+already defined eventop's.
+
+
diff --git a/Win32Build/mingw/README b/Win32Build/mingw/README
new file mode 100644
index 0000000000..67e75cd27e
--- /dev/null
+++ b/Win32Build/mingw/README
@@ -0,0 +1,8 @@
+The current SVN version of Tor should compile with MinGW.
+
+OpenSSL and libz both compile on MinGW out of the box.
+
+libevent 1.1b will not build unless you apply the diff in this directory.
+
+
+
diff --git a/Win32Build/mingw/libevent-1.1b-mingw-custom.diff b/Win32Build/mingw/libevent-1.1b-mingw-custom.diff
new file mode 100644
index 0000000000..350586e35f
--- /dev/null
+++ b/Win32Build/mingw/libevent-1.1b-mingw-custom.diff
@@ -0,0 +1,338 @@
+Only in libevent-1.1b: CHANGES
+Only in libevent-1.1b: Makefile
+diff -uwr libevent-1.1b-old/Makefile.am libevent-1.1b/Makefile.am
+--- libevent-1.1b-old/Makefile.am Wed Aug 9 22:16:35 2006
++++ libevent-1.1b/Makefile.am Sat Sep 2 03:49:26 2006
+@@ -1,6 +1,5 @@
+ AUTOMAKE_OPTIONS = foreign no-dependencies
+
+-SUBDIRS = . sample test
+
+ EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \
+ kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
+@@ -20,8 +19,29 @@
+
+ lib_LTLIBRARIES = libevent.la
+
+-libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c
+-libevent_la_LIBADD = @LTLIBOBJS@
++
++if BUILD_WIN32
++
++SUBDIRS = . sample
++SYS_LIBS = -lws2_32
++SYS_SRC = WIN32-Code/misc.c WIN32-Code/win32.c
++
++else
++
++SUBDIRS = . sample test
++SYS_LIBS =
++SYS_SRC =
++
++endif
++
++if USE_CUSTOM_CODE
++CUST_SRC = custom/custom.c
++else
++CUST_SRC =
++endif
++
++libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c $(CUST_SRC) $(SYS_SRC)
++libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
+ libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:2:0
+
+ include_HEADERS = event.h
+Only in libevent-1.1b: Makefile.in
+diff -uwr libevent-1.1b-old/WIN32-Code/misc.c libevent-1.1b/WIN32-Code/misc.c
+--- libevent-1.1b-old/WIN32-Code/misc.c Wed Aug 9 21:01:14 2006
++++ libevent-1.1b/WIN32-Code/misc.c Fri Sep 1 22:21:31 2006
+@@ -4,6 +4,12 @@
+ #include <sys/timeb.h>
+ #include <time.h>
+
++#ifdef __GNUC__
++/*our prototypes for timeval and timezone are in here, just in case the above
++ headers don't have them*/
++#include "misc.h"
++#endif
++
+ /****************************************************************************
+ *
+ * Function: gettimeofday(struct timeval *, struct timezone *)
+diff -uwr libevent-1.1b-old/WIN32-Code/misc.h libevent-1.1b/WIN32-Code/misc.h
+--- libevent-1.1b-old/WIN32-Code/misc.h Wed Aug 9 21:01:14 2006
++++ libevent-1.1b/WIN32-Code/misc.h Fri Sep 1 18:47:09 2006
+@@ -1,6 +1,9 @@
+ #ifndef MISC_H
+ #define MISC_H
+
++struct timezone;
++struct timeval;
++
+ int gettimeofday(struct timeval *,struct timezone *);
+
+ #endif
+diff -uwr libevent-1.1b-old/WIN32-Code/win32.c libevent-1.1b/WIN32-Code/win32.c
+--- libevent-1.1b-old/WIN32-Code/win32.c Wed Aug 9 21:25:48 2006
++++ libevent-1.1b/WIN32-Code/win32.c Sat Sep 2 00:45:55 2006
+@@ -60,7 +60,8 @@
+ /* MSDN says this is required to handle SIGFPE */
+ volatile double SIGFPE_REQ = 0.0f;
+
+-int signal_handler(int sig);
++static int signal_handler(int sig);
++
+ void signal_process(void);
+ int signal_recalc(void);
+
+@@ -77,20 +78,21 @@
+ };
+
+ void *win32_init (void);
+-int win32_insert (void *, struct event *);
+-int win32_del (void *, struct event *);
++int win32_insert (struct win32op *, struct event *);
++int win32_del (struct win32op *, struct event *);
+ int win32_recalc (struct event_base *base, void *, int);
+-int win32_dispatch (struct event_base *base, void *, struct timeval *);
++int win32_dispatch (struct event_base *base, struct win32op *, struct timeval *);
+
+ struct eventop win32ops = {
+ "win32",
+ win32_init,
+- win32_insert,
+- win32_del,
++ (int (*) (void*, struct event*)) win32_insert,
++ (int (*) (void*, struct event*)) win32_del,
+ win32_recalc,
+- win32_dispatch
++ (int (*) (struct event_base*, void*, struct timeval*)) win32_dispatch
+ };
+
++
+ #define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET)))
+
+ static int
+@@ -213,7 +215,13 @@
+ if (ev->ev_events & (EV_READ|EV_WRITE))
+ event_errx(1, "%s: EV_SIGNAL incompatible use",
+ __func__);
++
++#ifndef __GNUC__
+ if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1)
++#else
++ if((int)signal(EVENT_SIGNAL(ev), (void*) signal_handler) == -1)
++#endif
++
+ return (-1);
+
+ return (0);
+@@ -382,8 +390,13 @@
+
+ /* Reinstall our signal handler. */
+ TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
++#ifndef __GNUC__
+ if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1)
++#else
++ if((int)signal(EVENT_SIGNAL(ev), (void*) signal_handler) == -1)
++#endif
+ return (-1);
++
+ }
+ return (0);
+ }
+Only in libevent-1.1b-old/: aclocal.m4
+Only in libevent-1.1b: autom4te.cache
+diff -uwr libevent-1.1b-old/buffer.c libevent-1.1b/buffer.c
+--- libevent-1.1b-old/buffer.c Wed Aug 9 22:01:40 2006
++++ libevent-1.1b/buffer.c Fri Sep 1 18:52:56 2006
+@@ -197,7 +197,7 @@
+ u_char *data = EVBUFFER_DATA(buffer);
+ size_t len = EVBUFFER_LENGTH(buffer);
+ char *line;
+- u_int i;
++ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\r' || data[i] == '\n')
+Only in libevent-1.1b: config.guess
+Only in libevent-1.1b: config.h
+diff -uwr libevent-1.1b-old/config.h.in libevent-1.1b/config.h.in
+--- libevent-1.1b-old/config.h.in Wed Aug 9 21:27:37 2006
++++ libevent-1.1b/config.h.in Sat Sep 2 02:23:17 2006
+@@ -223,6 +223,9 @@
+ /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+ #undef TIME_WITH_SYS_TIME
+
++/* Define to 1 if you want to use a custom eventops variable */
++#undef USE_CUSTOM_EVENTOPS
++
+ /* Version number of package */
+ #undef VERSION
+
+@@ -232,11 +235,9 @@
+ /* Define to empty if `const' does not conform to ANSI C. */
+ #undef const
+
+-/* Define to `__inline__' or `__inline' if that's what the C compiler
+- calls it, or to nothing if 'inline' is not supported under any name. */
+-#ifndef __cplusplus
++/* Define as `__inline' if that's what the C compiler calls it, or to nothing
++ if it is not supported. */
+ #undef inline
+-#endif
+
+ /* Define to `int' if <sys/types.h> does not define. */
+ #undef pid_t
+Only in libevent-1.1b: config.h.in~
+Only in libevent-1.1b: config.log
+Only in libevent-1.1b: config.status
+Only in libevent-1.1b: configure
+diff -uwr libevent-1.1b-old/configure.in libevent-1.1b/configure.in
+--- libevent-1.1b-old/configure.in Wed Aug 9 22:05:17 2006
++++ libevent-1.1b/configure.in Sat Sep 2 03:40:15 2006
+@@ -21,6 +21,18 @@
+ CFLAGS="$CFLAGS -Wall"
+ fi
+
++AC_ARG_ENABLE(custom-eventops,
++ [ --enable-custom-eventops Use custom eventops variable],
++ AC_DEFINE([USE_CUSTOM_EVENTOPS],[1],
++ [Define to 1 to use a custom eventops variable])
++ ,)
++AC_ARG_ENABLE(custom-code,
++ [ --enable-custom-code Use custom code from custom/],
++ customcodev=true,
++ customcodev=false)
++
++AM_CONDITIONAL(USE_CUSTOM_CODE, test x$customcodev = xtrue)
++
+ AC_PROG_LIBTOOL
+
+ dnl Uncomment "AC_DISABLE_SHARED" to make shared librraries not get
+@@ -110,6 +122,22 @@
+ AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+ )
+ fi
++
++dnl - check if the macro WIN32 is defined on this compiler.
++dnl - (this is how we check for a windows version of GCC)
++AC_MSG_CHECKING(for WIN32)
++AC_TRY_COMPILE(,
++ [
++ #ifndef WIN32
++ #error
++ #endif
++ ],
++ bwin32=true; AC_MSG_RESULT(yes),
++ bwin32=false; AC_MSG_RESULT(no),
++)
++
++AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue)
++
+
+ dnl Checks for typedefs, structures, and compiler characteristics.
+ AC_C_CONST
+diff -uwr libevent-1.1b-old/evbuffer.c libevent-1.1b/evbuffer.c
+--- libevent-1.1b-old/evbuffer.c Wed Aug 9 21:01:14 2006
++++ libevent-1.1b/evbuffer.c Fri Sep 1 19:18:13 2006
+@@ -154,12 +154,20 @@
+ if (EVBUFFER_LENGTH(bufev->output)) {
+ res = evbuffer_write(bufev->output, fd);
+ if (res == -1) {
++#ifndef WIN32
++/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
++ *set errno. thus this error checking is not portable*/
+ if (errno == EAGAIN ||
+ errno == EINTR ||
+ errno == EINPROGRESS)
+ goto reschedule;
+ /* error case */
+ what |= EVBUFFER_ERROR;
++
++#else
++ goto reschedule;
++#endif
++
+ } else if (res == 0) {
+ /* eof case */
+ what |= EVBUFFER_EOF;
+@@ -181,6 +189,7 @@
+ return;
+
+ reschedule:
++
+ if (EVBUFFER_LENGTH(bufev->output) != 0)
+ bufferevent_add(&bufev->ev_write, bufev->timeout_write);
+ return;
+diff -uwr libevent-1.1b-old/event.c libevent-1.1b/event.c
+--- libevent-1.1b-old/event.c Wed Aug 9 21:25:48 2006
++++ libevent-1.1b/event.c Sat Sep 2 04:22:05 2006
+@@ -30,8 +30,14 @@
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #undef WIN32_LEAN_AND_MEAN
++
++#ifdef __GNUC__
++#include "WIN32-Code/misc.h"
++#else
+ #include "misc.h"
+ #endif
++
++#endif
+ #include <sys/types.h>
+ #include <sys/tree.h>
+ #ifdef HAVE_SYS_TIME_H
+@@ -53,6 +59,7 @@
+ #include "event-internal.h"
+ #include "log.h"
+
++
+ #ifdef HAVE_SELECT
+ extern const struct eventop selectops;
+ #endif
+@@ -75,6 +82,8 @@
+ extern const struct eventop win32ops;
+ #endif
+
++#ifndef USE_CUSTOM_EVENTOPS
++
+ /* In order of preference */
+ const struct eventop *eventops[] = {
+ #ifdef HAVE_WORKING_KQUEUE
+@@ -101,6 +110,11 @@
+ NULL
+ };
+
++#else
++#include "custom-eventops.h"
++#endif //USE_CUSTOM_EVENTOPS
++
++
+ /* Global state */
+ struct event_list signalqueue;
+
+Only in libevent-1.1b: libtool
+diff -uwr libevent-1.1b-old/log.c libevent-1.1b/log.c
+--- libevent-1.1b-old/log.c Wed Aug 9 21:01:14 2006
++++ libevent-1.1b/log.c Fri Sep 1 19:09:45 2006
+@@ -45,8 +45,14 @@
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #undef WIN32_LEAN_AND_MEAN
++
++#ifdef __GNUC__
++#include "WIN32-Code/misc.h"
++#else
+ #include "misc.h"
+ #endif
++
++#endif
+ #include <sys/types.h>
+ #include <sys/tree.h>
+ #ifdef HAVE_SYS_TIME_H
+Only in libevent-1.1b/sample: Makefile
+Only in libevent-1.1b/sample: Makefile.in
+Only in libevent-1.1b: stamp-h1
+Only in libevent-1.1b/test: Makefile
+Only in libevent-1.1b/test: Makefile.in
diff --git a/Win32Build/mingw/libevent-1.1b-mingw.diff b/Win32Build/mingw/libevent-1.1b-mingw.diff
new file mode 100644
index 0000000000..c7cea5b326
--- /dev/null
+++ b/Win32Build/mingw/libevent-1.1b-mingw.diff
@@ -0,0 +1,221 @@
+=== Makefile.am
+==================================================================
+--- Makefile.am (revision 8794)
++++ Makefile.am (local)
+@@ -1,6 +1,5 @@
+ AUTOMAKE_OPTIONS = foreign no-dependencies
+
+-SUBDIRS = . sample test
+
+ EXTRA_DIST = acconfig.h event.h event-internal.h log.h evsignal.h event.3 \
+ kqueue.c epoll_sub.c epoll.c select.c rtsig.c poll.c signal.c \
+@@ -20,13 +19,29 @@
+
+ lib_LTLIBRARIES = libevent.la
+
+-libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c
+-libevent_la_LIBADD = @LTLIBOBJS@
++if BUILD_WIN32
++
++SUBDIRS = . sample
++SYS_LIBS = -lws2_32
++SYS_SRC = WIN32-Code/misc.c WIN32-Code/win32.c
++SYS_INCLUDES = -IWIN32-Code
++
++else
++
++SUBDIRS = . sample test
++SYS_LIBS =
++SYS_SRC =
++SYS_INCLUDES =
++
++endif
++
++libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c $(SYS_SRC)
++libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
+ libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:2:0
+
+ include_HEADERS = event.h
+
+-INCLUDES = -Icompat
++INCLUDES = -Icompat $(SYS_INCLUDES)
+
+ man_MANS = event.3
+
+=== WIN32-Code/misc.c
+==================================================================
+--- WIN32-Code/misc.c (revision 8794)
++++ WIN32-Code/misc.c (local)
+@@ -4,6 +4,12 @@
+ #include <sys/timeb.h>
+ #include <time.h>
+
++#ifdef __GNUC__
++/*our prototypes for timeval and timezone are in here, just in case the above
++ headers don't have them*/
++#include "misc.h"
++#endif
++
+ /****************************************************************************
+ *
+ * Function: gettimeofday(struct timeval *, struct timezone *)
+@@ -17,6 +23,7 @@
+ *
+ ****************************************************************************/
+
++#ifndef HAVE_GETTIMEOFDAY
+ int gettimeofday(struct timeval *tv, struct timezone *tz) {
+ struct _timeb tb;
+
+@@ -28,6 +35,7 @@
+ tv->tv_usec = ((int) tb.millitm) * 1000;
+ return 0;
+ }
++#endif
+
+ int
+ win_read(int fd, void *buf, unsigned int length)
+=== WIN32-Code/misc.h
+==================================================================
+--- WIN32-Code/misc.h (revision 8794)
++++ WIN32-Code/misc.h (local)
+@@ -1,6 +1,11 @@
+ #ifndef MISC_H
+ #define MISC_H
+
++struct timezone;
++struct timeval;
++
++#ifndef HAVE_GETTIMEOFDAY
+ int gettimeofday(struct timeval *,struct timezone *);
++#endif
+
+ #endif
+=== WIN32-Code/win32.c
+==================================================================
+--- WIN32-Code/win32.c (revision 8794)
++++ WIN32-Code/win32.c (local)
+@@ -60,7 +60,8 @@
+ /* MSDN says this is required to handle SIGFPE */
+ volatile double SIGFPE_REQ = 0.0f;
+
+-int signal_handler(int sig);
++static void signal_handler(int sig);
++
+ void signal_process(void);
+ int signal_recalc(void);
+
+@@ -205,8 +206,9 @@
+ }
+
+ int
+-win32_insert(struct win32op *win32op, struct event *ev)
++win32_insert(void *op, struct event *ev)
+ {
++ struct win32op *win32op = op;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL) {
+@@ -251,8 +253,9 @@
+ }
+
+ int
+-win32_del(struct win32op *win32op, struct event *ev)
++win32_del(void *op, struct event *ev)
+ {
++ struct win32op *win32op = op;
+ int i, found;
+
+ if (ev->ev_events & EV_SIGNAL)
+@@ -302,9 +305,10 @@
+ */
+
+ int
+-win32_dispatch(struct event_base *base, struct win32op *win32op,
++win32_dispatch(struct event_base *base, void *op,
+ struct timeval *tv)
+ {
++ struct win32op *win32op = op;
+ int res = 0;
+ int i;
+ int fd_count;
+@@ -366,13 +370,11 @@
+ }
+
+
+-static int
++static void
+ signal_handler(int sig)
+ {
+ evsigcaught[sig]++;
+ signal_caught = 1;
+-
+- return 0;
+ }
+
+ int
+=== buffer.c
+==================================================================
+--- buffer.c (revision 8794)
++++ buffer.c (local)
+@@ -197,7 +197,7 @@
+ u_char *data = EVBUFFER_DATA(buffer);
+ size_t len = EVBUFFER_LENGTH(buffer);
+ char *line;
+- u_int i;
++ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\r' || data[i] == '\n')
+=== configure.in
+==================================================================
+--- configure.in (revision 8794)
++++ configure.in (local)
+@@ -111,6 +111,21 @@
+ )
+ fi
+
++dnl - check if the macro WIN32 is defined on this compiler.
++dnl - (this is how we check for a windows version of GCC)
++AC_MSG_CHECKING(for WIN32)
++AC_TRY_COMPILE(,
++ [
++ #ifndef WIN32
++ #error
++ #endif
++ ],
++ bwin32=true; AC_MSG_RESULT(yes),
++ bwin32=false; AC_MSG_RESULT(no),
++)
++
++AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue)
++
+ dnl Checks for typedefs, structures, and compiler characteristics.
+ AC_C_CONST
+ AC_C_INLINE
+=== evbuffer.c
+==================================================================
+--- evbuffer.c (revision 8794)
++++ evbuffer.c (local)
+@@ -154,12 +154,20 @@
+ if (EVBUFFER_LENGTH(bufev->output)) {
+ res = evbuffer_write(bufev->output, fd);
+ if (res == -1) {
++#ifndef WIN32
++/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
++ *set errno. thus this error checking is not portable*/
+ if (errno == EAGAIN ||
+ errno == EINTR ||
+ errno == EINPROGRESS)
+ goto reschedule;
+ /* error case */
+ what |= EVBUFFER_ERROR;
++
++#else
++ goto reschedule;
++#endif
++
+ } else if (res == 0) {
+ /* eof case */
+ what |= EVBUFFER_EOF;
+
diff --git a/Win32Build/mingw/libevent-svn-mingw.diff b/Win32Build/mingw/libevent-svn-mingw.diff
new file mode 100644
index 0000000000..af4ffbbbb5
--- /dev/null
+++ b/Win32Build/mingw/libevent-svn-mingw.diff
@@ -0,0 +1,210 @@
+=== Makefile.am
+==================================================================
+--- Makefile.am (revision 8794)
++++ Makefile.am (local)
+@@ -1,6 +1,5 @@
+ AUTOMAKE_OPTIONS = foreign no-dependencies
+
+-SUBDIRS = . sample test
+
+ bin_SCRIPTS = event_rpcgen.py
+
+@@ -22,18 +21,34 @@
+
+ lib_LTLIBRARIES = libevent.la
+
++if BUILD_WIN32
++
++SUBDIRS = . sample
++SYS_LIBS = -lws2_32
++SYS_SRC = WIN32-Code/misc.c WIN32-Code/win32.c
++SYS_INCLUDES = -IWIN32-Code
++
++else
++
++SUBDIRS = . sample test
++SYS_LIBS =
++SYS_SRC =
++SYS_INCLUDES =
++
++endif
++
+ libevent_la_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \
+- http.c evhttp.h http-internal.h evdns.c evdns.h
+-libevent_la_LIBADD = @LTLIBOBJS@
++ http.c evhttp.h http-internal.h evdns.c evdns.h $(SYS_SRC)
++libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS)
+ libevent_la_LDFLAGS = -release @VERSION@ -version-info 1:3:0
+
+ include_HEADERS = event.h evhttp.h evdns.h
+
+-INCLUDES = -Icompat
++INCLUDES = -Icompat $(SYS_INCLUDES)
+
+ man_MANS = event.3
+
+ verify: libevent.la
+- cd $(srcdir)/test && make verify
++ cd $(srcdir)/test && make verify
+
+ DISTCLEANFILES = *~
+=== WIN32-Code/misc.c
+==================================================================
+--- WIN32-Code/misc.c (revision 8794)
++++ WIN32-Code/misc.c (local)
+@@ -4,6 +4,12 @@
+ #include <sys/timeb.h>
+ #include <time.h>
+
++#ifdef __GNUC__
++/*our prototypes for timeval and timezone are in here, just in case the above
++ headers don't have them*/
++#include "misc.h"
++#endif
++
+ /****************************************************************************
+ *
+ * Function: gettimeofday(struct timeval *, struct timezone *)
+=== WIN32-Code/misc.h
+==================================================================
+--- WIN32-Code/misc.h (revision 8794)
++++ WIN32-Code/misc.h (local)
+@@ -1,6 +1,9 @@
+ #ifndef MISC_H
+ #define MISC_H
+
++struct timezone;
++struct timeval;
++
+ int gettimeofday(struct timeval *,struct timezone *);
+
+ #endif
+=== WIN32-Code/win32.c
+==================================================================
+--- WIN32-Code/win32.c (revision 8794)
++++ WIN32-Code/win32.c (local)
+@@ -60,7 +60,8 @@
+ /* MSDN says this is required to handle SIGFPE */
+ volatile double SIGFPE_REQ = 0.0f;
+
+-int signal_handler(int sig);
++static void signal_handler(int sig);
++
+ void signal_process(void);
+ int signal_recalc(void);
+
+@@ -207,8 +208,9 @@
+ }
+
+ int
+-win32_insert(struct win32op *win32op, struct event *ev)
++win32_insert(void *op, struct event *ev)
+ {
++ struct win32op *win32op = op;
+ int i;
+
+ if (ev->ev_events & EV_SIGNAL) {
+@@ -253,8 +255,9 @@
+ }
+
+ int
+-win32_del(struct win32op *win32op, struct event *ev)
++win32_del(void *op, struct event *ev)
+ {
++ struct win32op *win32op = op;
+ int i, found;
+
+ if (ev->ev_events & EV_SIGNAL)
+@@ -304,9 +307,10 @@
+ */
+
+ int
+-win32_dispatch(struct event_base *base, struct win32op *win32op,
++win32_dispatch(struct event_base *base, void *op,
+ struct timeval *tv)
+ {
++ struct win32op *win32op = op;
+ int res = 0;
+ int i;
+ int fd_count;
+@@ -389,13 +393,11 @@
+ free(win32op);
+ }
+
+-static int
++static void
+ signal_handler(int sig)
+ {
+ evsigcaught[sig]++;
+ signal_caught = 1;
+-
+- return 0;
+ }
+
+ int
+=== buffer.c
+==================================================================
+--- buffer.c (revision 8794)
++++ buffer.c (local)
+@@ -197,7 +197,7 @@
+ u_char *data = EVBUFFER_DATA(buffer);
+ size_t len = EVBUFFER_LENGTH(buffer);
+ char *line;
+- u_int i;
++ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\r' || data[i] == '\n')
+=== configure.in
+==================================================================
+--- configure.in (revision 8794)
++++ configure.in (local)
+@@ -111,6 +111,22 @@
+ )
+ fi
+
++dnl - check if the macro WIN32 is defined on this compiler.
++dnl - (this is how we check for a windows version of GCC)
++AC_MSG_CHECKING(for WIN32)
++AC_TRY_COMPILE(,
++ [
++ #ifndef WIN32
++ #error
++ #endif
++ ],
++ bwin32=true; AC_MSG_RESULT(yes),
++ bwin32=false; AC_MSG_RESULT(no),
++)
++
++AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue)
++
++
+ dnl Checks for typedefs, structures, and compiler characteristics.
+ AC_C_CONST
+ AC_C_INLINE
+=== evbuffer.c
+==================================================================
+--- evbuffer.c (revision 8794)
++++ evbuffer.c (local)
+@@ -163,12 +162,20 @@
+ if (EVBUFFER_LENGTH(bufev->output)) {
+ res = evbuffer_write(bufev->output, fd);
+ if (res == -1) {
++#ifndef WIN32
++/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not
++ *set errno. thus this error checking is not portable*/
+ if (errno == EAGAIN ||
+ errno == EINTR ||
+ errno == EINPROGRESS)
+ goto reschedule;
+ /* error case */
+ what |= EVBUFFER_ERROR;
++
++#else
++ goto reschedule;
++#endif
++
+ } else if (res == 0) {
+ /* eof case */
+ what |= EVBUFFER_EOF;
+
diff --git a/Win32Build/vc6/Tor.dsw b/Win32Build/vc6/Tor.dsw
index 3999da18c2..3999da18c2 100755..100644
--- a/Win32Build/vc6/Tor.dsw
+++ b/Win32Build/vc6/Tor.dsw
diff --git a/Win32Build/vc6/tor/Tor.dsp b/Win32Build/vc6/tor/Tor.dsp
index 4df3052ecc..f05e04931d 100755..100644
--- a/Win32Build/vc6/tor/Tor.dsp
+++ b/Win32Build/vc6/tor/Tor.dsp
@@ -106,10 +106,6 @@ SOURCE=..\..\..\src\common\crypto.c
# End Source File
# Begin Source File
-SOURCE=..\..\..\src\common\fakepoll.c
-# End Source File
-# Begin Source File
-
SOURCE=..\..\..\src\common\log.c
# End Source File
# Begin Source File
@@ -294,6 +290,10 @@ SOURCE=..\..\..\src\or\onion.c
# End Source File
# Begin Source File
+SOURCE=..\..\..\src\or\policies.c
+# End Source File
+# Begin Source File
+
SOURCE=..\..\..\src\or\relay.c
# End Source File
# Begin Source File
diff --git a/Win32Build/vc6/tor_resolve/tor_resolve.dsp b/Win32Build/vc6/tor_resolve/tor_resolve.dsp
index 63f406b9e1..63f406b9e1 100755..100644
--- a/Win32Build/vc6/tor_resolve/tor_resolve.dsp
+++ b/Win32Build/vc6/tor_resolve/tor_resolve.dsp
diff --git a/Win32Build/vc7/Tor/Tor.sln b/Win32Build/vc7/Tor/Tor.sln
index 3087e26cc1..3087e26cc1 100755..100644
--- a/Win32Build/vc7/Tor/Tor.sln
+++ b/Win32Build/vc7/Tor/Tor.sln
diff --git a/Win32Build/vc7/Tor/Tor.vcproj b/Win32Build/vc7/Tor/Tor.vcproj
index 307521e89d..307521e89d 100755..100644
--- a/Win32Build/vc7/Tor/Tor.vcproj
+++ b/Win32Build/vc7/Tor/Tor.vcproj
diff --git a/Win32Build/vc7/tor_resolve/tor_resolve.vcproj b/Win32Build/vc7/tor_resolve/tor_resolve.vcproj
index a44f1ac66a..a44f1ac66a 100755..100644
--- a/Win32Build/vc7/tor_resolve/tor_resolve.vcproj
+++ b/Win32Build/vc7/tor_resolve/tor_resolve.vcproj
diff --git a/Win32Build/vc7/unittests/unittests.vcproj b/Win32Build/vc7/unittests/unittests.vcproj
index 3dffa786cc..3dffa786cc 100755..100644
--- a/Win32Build/vc7/unittests/unittests.vcproj
+++ b/Win32Build/vc7/unittests/unittests.vcproj
diff --git a/configure.in b/configure.in
index eadbd09d3d..7527bed5cc 100644
--- a/configure.in
+++ b/configure.in
@@ -4,7 +4,7 @@ dnl Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
dnl See LICENSE for licensing information
AC_INIT
-AM_INIT_AUTOMAKE(tor, 0.1.1.23)
+AM_INIT_AUTOMAKE(tor, 0.1.2.1-alpha-dev)
AM_CONFIG_HEADER(orconfig.h)
AC_CANONICAL_HOST
@@ -44,6 +44,26 @@ if test $enable_threads = "yes"; then
AC_DEFINE(ENABLE_THREADS, 1, [Defined if we will try to use multithreading])
fi
+AC_ARG_ENABLE(eventdns,
+ AC_HELP_STRING(--enable-eventdns, enable asynchronous dns module),
+ [case "${enableval}" in
+ yes) eventdns=true ;;
+ no) eventdns=false ;;
+ *) AC_MSG_ERROR(bad value for --enable-eventdns) ;;
+ esac], [eventdns=true])
+AM_CONDITIONAL(EVENTDNS, test x$eventdns = xtrue)
+if test x$eventdns = xtrue; then
+ AC_DEFINE([USE_EVENTDNS], 1, "Define to 1 if we'll be using eventdns.c")
+fi
+
+AC_ARG_ENABLE(transparent,
+ AC_HELP_STRING(--disable-transparent, disable transparent proxy support),
+ [case "${enableval}" in
+ yes) transparent=true ;;
+ no) transparent=false ;;
+ *) AC_MSG_ERROR(bad value for --enable-transparent) ;;
+ esac], [transparent=true])
+
case $host in
*-*-solaris* )
AC_DEFINE(_REENTRANT, 1, [Define on some platforms to activate x_r() functions in time.h])
@@ -54,6 +74,31 @@ AC_PROG_CC
AC_PROG_MAKE_SET
AC_PROG_RANLIB
+# If WIN32 is defined and non-zero, we are building for win32
+AC_MSG_CHECKING([for win32])
+AC_TRY_COMPILE(,
+[
+#ifdef WIN32
+#if WIN32
+//all is well
+#else
+#error
+#endif
+#else
+#error
+#endif
+],
+bwin32=true; AC_MSG_RESULT([yes]),
+bwin32=false; AC_MSG_RESULT([no])
+)
+
+if test $bwin32 = true; then
+
+AC_DEFINE(MS_WINDOWS,1, [Define to 1 if we are building for a Windows platform.])
+
+
+fi
+
# The big search for OpenSSL
# copied from openssh's configure.ac
tryssldir=""
@@ -113,7 +158,12 @@ AC_CACHE_CHECK([for libevent directory], ac_cv_libevent_dir, [
le_found=no
for ledir in $trylibeventdir "" $prefix /usr/local ; do
LDFLAGS="$saved_LDFLAGS"
- LIBS="$saved_LIBS -levent"
+
+ if test $bwin32 = true; then
+ LIBS="$saved_LIBS -levent -lws2_32"
+ else
+ LIBS="$saved_LIBS -levent"
+ fi
# Skip the directory if it isn't there.
if test ! -z "$ledir" -a ! -d "$ledir" ; then
@@ -153,7 +203,12 @@ AC_CACHE_CHECK([for libevent directory], ac_cv_libevent_dir, [
AC_MSG_ERROR([Could not find a linkable libevent. You can specify an explicit path using --with-libevent-dir])
fi
])
+
+if test $bwin32 = true; then
+LIBS="$LIBS -levent -lws2_32"
+else
LIBS="$LIBS -levent"
+fi
if test $ac_cv_libevent_dir != "(system)"; then
if test -d "$ac_cv_libevent_dir/lib" ; then
LDFLAGS="-L$ac_cv_libevent_dir/lib $LDFLAGS"
@@ -169,6 +224,7 @@ if test $ac_cv_libevent_dir != "(system)"; then
fi
fi
+if test -z "$CROSS_COMPILE"; then
AC_CACHE_CHECK([whether we need extra options to link libevent],
ac_cv_libevent_linker_option, [
saved_LDFLAGS="$LDFLAGS"
@@ -200,6 +256,7 @@ AC_CACHE_CHECK([whether we need extra options to link libevent],
if test $ac_cv_libevent_linker_option != '(none)' ; then
LDFLAGS="$ac_cv_libevent_linker_option $LDFLAGS"
fi
+fi
dnl ------------------------------------------------------
dnl Where do you live, openssl? And how do we call you?
@@ -211,7 +268,12 @@ AC_CACHE_CHECK([for OpenSSL directory], ac_cv_openssl_dir, [
ssl_found=no
for ssldir in $tryssldir "" $prefix /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /usr/athena /usr/pkg /opt /opt/openssl ; do
LDFLAGS="$saved_LDFLAGS"
- LIBS="$saved_LIBS -lssl -lcrypto"
+
+ if test $bwin32 = true; then
+ LIBS="$saved_LIBS -lssl -lcrypto -lws2_32 -lgdi32"
+ else
+ LIBS="$saved_LIBS -lssl -lcrypto"
+ fi
# Skip the directory if it isn't there.
if test ! -z "$ssldir" -a ! -d "$ssldir" ; then
@@ -250,7 +312,13 @@ AC_CACHE_CHECK([for OpenSSL directory], ac_cv_openssl_dir, [
AC_MSG_ERROR([Could not find a linkable OpenSSL. You can specify an explicit path using --with-ssl-dir])
fi
])
+
+if test $bwin32 = true; then
+LIBS="$LIBS -lssl -lcrypto -lws2_32 -lgdi32"
+else
LIBS="$LIBS -lssl -lcrypto"
+fi
+
if test "$ac_cv_openssl_dir" != "(system)"; then
if test -d "$ac_cv_openssl_dir/lib" ; then
LDFLAGS="-L$ac_cv_openssl_dir/lib $LDFLAGS"
@@ -266,6 +334,8 @@ if test "$ac_cv_openssl_dir" != "(system)"; then
fi
fi
+if test -z $CROSS_COMPILE
+then
AC_CACHE_CHECK([whether we need extra options to link OpenSSL],
ac_cv_openssl_linker_option, [
saved_LDFLAGS="$LDFLAGS"
@@ -325,6 +395,7 @@ return (OPENSSL_VERSION_NUMBER == SSLeay()) == 0;
if test "$ac_cv_openssl_linker_option" != '(none)' ; then
LDFLAGS="$ac_cv_openssl_linker_option $LDFLAGS"
fi
+fi
dnl Make sure to enable support for large off_t if avalable.
@@ -334,21 +405,74 @@ dnl The warning message here is no longer strictly accurate.
AC_CHECK_HEADERS(unistd.h string.h signal.h netdb.h ctype.h sys/stat.h sys/types.h fcntl.h sys/fcntl.h sys/ioctl.h sys/socket.h sys/time.h netinet/in.h arpa/inet.h errno.h assert.h time.h pwd.h grp.h, , AC_MSG_WARN(some headers were not found, compilation may fail))
-AC_CHECK_HEADERS(event.h, , AC_MSG_ERROR(Libevent header (event.h) not found. Tor requires libevent to build.))
+AC_CHECK_HEADERS(event.h, , AC_MSG_ERROR(Libevent header (event.h) not found. Tor requires libevent to build.),
+[#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+ struct timeval {
+ long tv_sec;
+ long tv_usec;
+ };
+#endif])
AC_CHECK_HEADERS(zlib.h, , AC_MSG_ERROR(Zlib header (zlib.h) not found. Tor requires zlib to build. You may need to install a zlib development package.))
dnl These headers are not essential
-AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h syslog.h sys/time.h sys/resource.h stddef.h inttypes.h utime.h sys/utime.h)
+AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h limits.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h syslog.h sys/time.h sys/resource.h inttypes.h utime.h sys/utime.h sys/mman.h)
-AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit setrlimit strlcat strlcpy strtoull getpwnam getpwuid ftello getaddrinfo localtime_r gmtime_r event_get_version event_get_method event_set_log_callback memmem)
+AC_CHECK_HEADERS(net/if.h, [net_if_found=1], [net_if_found=0],
+[#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif])
+AC_CHECK_HEADERS(net/pfvar.h, [net_pfvar_found=1], [net_pfvar_found=0],
+[#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif])
+AC_CHECK_HEADERS(linux/netfilter_ipv4.h,
+ [linux_netfilter_ipv4=1], [linux_netfilter_ipv4=0],
+[#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif])
+
+AC_CHECK_FUNCS(gettimeofday ftime socketpair uname inet_aton strptime getrlimit strlcat strlcpy strtoull ftello getaddrinfo localtime_r gmtime_r event_get_version event_get_method event_set_log_callback memmem strtok_r)
if test $enable_threads = "yes"; then
AC_CHECK_HEADERS(pthread.h)
AC_CHECK_FUNCS(pthread_create)
fi
+if test x$transparent = xtrue ; then
+ transparent_ok=0
+ if test x$net_if_found = x1 -a x$net_pfvar_found = x1 ; then
+ transparent_ok=1
+ fi
+ if test x$linux_netfilter_ipv4 = x1 ; then
+ transparent_ok=1
+ fi
+ if test x$transparent_ok = x1 ; then
+ AC_DEFINE(USE_TRANSPARENT, 1, "Define to enable transparent proxy support")
+ case $host in
+ *-*-openbsd*)
+ AC_DEFINE(OPENBSD, 1, "Define to handle pf on OpenBSD properly") ;;
+ esac
+ else
+ AC_MSG_NOTICE([Transparent proxy support enabled, but missing headers.])
+ fi
+fi
+
AC_FUNC_FSEEKO
AC_CHECK_MEMBERS([struct timeval.tv_sec])
@@ -377,11 +501,20 @@ AC_CHECK_SIZEOF(long long)
AC_CHECK_SIZEOF(__int64)
AC_CHECK_SIZEOF(void *)
AC_CHECK_SIZEOF(time_t)
+
+AC_CHECK_TYPES([uint, u_char])
+
+if test -z "$CROSS_COMPILE"; then
AC_CACHE_CHECK([whether time_t is signed], tor_cv_time_t_signed, [
AC_TRY_RUN([
int main(int c, char**v) { if (((time_t)-1)<0) return 1; else return 0; }],
tor_cv_time_t_signed=no, tor_cv_time_t_signed=yes)
])
+else
+ # Cross-compiling; let's hope the target platform isn't loony.
+ AC_MSG_NOTICE([Cross compiling: assuming that time_t is signed.])
+ tor_cv_time_t_signed=yes
+fi
if test $tor_cv_time_t_signed = yes; then
AC_DEFINE([TIME_T_IS_SIGNED], 1,
@@ -398,36 +531,8 @@ AC_CHECK_SIZEOF(socklen_t, , [AC_INCLUDES_DEFAULT()
AC_CHECK_SIZEOF(cell_t)
-# Now, let's see about alignment requirements. On some platforms, we override
-# the default.
-case $host in
- ia64-*-* | arm-*-* | sparc-*-* | sparc64-*-* )
- tor_cv_unaligned_ok=no
- ;;
- # On the following architectures unaligned access works, but is not done in
- # hardware. This means that when you try to do unaligned access the kernel
- # gets to sort out an exception and then work around to somehow make your
- # reqest work, which is quite expensive. Therefore it's probably better to
- # not even do it.
- alpha-*-* | mips-*-* | mipsel-*-* )
- tor_cv_unaligned_ok=no
- ;;
- *)
-AC_CACHE_CHECK([whether unaligned int access is allowed], tor_cv_unaligned_ok,
-[AC_RUN_IFELSE([AC_LANG_SOURCE(
-[[int main () { char s[] = "A\x00\x00\x00\x00\x00\x00\x00";
-return *(int*)(&s[1]); }]])],
- [tor_cv_unaligned_ok=yes],
- [tor_cv_unaligned_ok=no],
- [tor_cv_unaligned_ok=cross])])
-esac
-
-if test $tor_cv_unaligned_ok = yes; then
- AC_DEFINE([UNALIGNED_INT_ACCESS_OK], 1,
- [Define to 1 iff unaligned int access is allowed])
-fi
-
# Now make sure that NULL can be represented as zero bytes.
+if test -z "$CROSS_COMPILE"; then
AC_CACHE_CHECK([whether memset(0) sets pointers to NULL], tor_cv_null_is_zero,
[AC_RUN_IFELSE([AC_LANG_SOURCE(
[[#include <stdlib.h>
@@ -442,11 +547,44 @@ return memcmp(&p1,&p2,sizeof(char*))?1:0; }]])],
[tor_cv_null_is_zero=no],
[tor_cv_null_is_zero=cross])])
+else
+ # Cross-compiling; let's hope that the target isn't raving mad.
+ AC_MSG_NOTICE([Cross-compiling: we'll assume that NULL is represented as a sequence of 0-valued bytes.])
+ tor_cv_null_is_zero=yes
+fi
+
if test $tor_cv_null_is_zero = yes; then
AC_DEFINE([NULL_REP_IS_ZERO_BYTES], 1,
[Define to 1 iff memset(0) sets pointers to NULL])
fi
+# And what happens when we malloc zero?
+
+if test -z "$CROSS_COMPILE"; then
+AC_CACHE_CHECK([whether we can malloc(0) safely.], tor_cv_malloc_zero_works,
+[AC_RUN_IFELSE([AC_LANG_SOURCE(
+[[#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+int main () { return malloc(0)?0:1; }]])],
+ [tor_cv_malloc_zero_works=yes],
+ [tor_cv_malloc_zero_works=no],
+ [tor_cv_malloc_zero_works=cross])])
+
+else
+ # Cross-compiling; let's hope that the target isn't raving mad.
+ AC_MSG_NOTICE([Cross-compiling: we'll assume that we need to check malloc() arguments for 0.])
+ tor_cv_malloc_zero_works=no
+fi
+
+if test $tor_cv_malloc_zero_works = yes; then
+ AC_DEFINE([MALLOC_ZERO_WORKS], 1,
+ [Define to 1 iff malloc(0) returns a pointer])
+fi
+
# Whether we should use the dmalloc memory allocation debugging library.
AC_MSG_CHECKING(whether to use dmalloc (debug memory allocation library))
AC_ARG_WITH(dmalloc,
@@ -557,7 +695,6 @@ if test $ac_cv_have_func_macro = 'yes'; then
AC_DEFINE(HAVE_MACRO__func__, 1, [Defined if the compiler supports __func__])
fi
-
if test $ac_cv_have_FUNC_macro = 'yes'; then
AC_DEFINE(HAVE_MACRO__FUNC__, 1, [Defined if the compiler supports __FUNC__])
fi
@@ -567,7 +704,6 @@ if test $ac_cv_have_FUNCTION_macro = 'yes'; then
[Defined if the compiler supports __FUNCTION__])
fi
-
# $prefix stores the value of the --prefix command line option, or
# NONE if the option wasn't set. In the case that it wasn't set, make
# it be the default, so that we can use it to expand directories now.
@@ -602,7 +738,7 @@ else
fi
# Add some more warnings which we use in the cvs version but not in the
# released versions. (Some relevant gcc versions can't handle these.)
-#CFLAGS="$CFLAGS -W -Wno-unused-parameter -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat=2 -Winit-self -Wwrite-strings -Waggregate-return -Wmissing-declarations -Wmissing-field-initializers -Wredundant-decls -Winline"
+#CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat=2 -Winit-self -Wwrite-strings -Waggregate-return -Wmissing-declarations -Wmissing-field-initializers -Wredundant-decls -Winline -Wnested-externs -Wswitch-enums"
# Add these in when you feel like fun.
#CFLAGS="$CFLAGS -Wbad-function-cast -Werror -Wdeclaration-after-statement -Wold-style-definition"
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 16e9158cd9..d8ae4c06d6 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -3,7 +3,7 @@ DIST_SUBDIRS = osx suse
confdir = $(sysconfdir)/tor
-EXTRA_DIST = exitlist tor-tsocks.conf torify.1 TorControl.py tor.nsi.in tor.sh torctl rc.subr ExerciseServer.py PathDemo.py
+EXTRA_DIST = exitlist tor-tsocks.conf torify.1 TorControl.py tor.nsi.in tor.sh torctl rc.subr ExerciseServer.py PathDemo.py cross.sh
conf_DATA = tor-tsocks.conf
diff --git a/contrib/add-tor b/contrib/add-tor
new file mode 100755
index 0000000000..5a12abca80
--- /dev/null
+++ b/contrib/add-tor
@@ -0,0 +1,115 @@
+#!/usr/bin/ruby
+
+# add-tor - Add a tor fingerprint line to the approved-routers file
+#
+# Tor's approved-routers file is expected to be versioned using RCS.
+# This script checks for uncommitted changes, does a checkout of the
+# file, adds the new fingerprint with a comment stating the server's
+# operator, and commits the file to RCS again (using -u so that the
+# working copy is not removed.
+#
+# Operator and fingerprint line are read from stdin.
+#
+# Before adding a fingerprint line, approved-routers is checked for
+# rough syntactical correctness. This script also checks that the
+# nickname and fingerprint to be added do not already exist in the
+# binding list.
+
+
+# Copyright (c) 2006 by Peter Palfrader
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+BINDING = '/etc/tor/approved-routers'
+
+def mysys(cmd)
+ unless system(cmd)
+ STDERR.puts "ERROR: #{cmd} failed"
+ exit 1
+ end
+end
+
+def check_nick(n)
+ n =~ /^[a-zA-Z0-9]+$/
+end
+
+def check_fpr(fpr)
+ fpr =~ /^([0-9A-F]{4} ){9}[0-9A-F]{4}$/
+end
+
+def parse_fprline(fprline)
+ n = fprline[0 ... fprline.index(' ')]
+ f = fprline[fprline.index(' ') + 1 .. -1 ]
+ unless check_nick(n) and check_fpr(f)
+ STDERR.puts "Invalid fpr syntax '#{fprline}'"
+ exit 1
+ end
+ [n, f]
+end
+
+
+
+unless system("rcsdiff -q -u #{BINDING}")
+ STDERR.puts "Uncommitted changes in #{BINDING}. Aborting."
+ exit 1
+end
+
+puts "Checking out #{BINDING}..."
+mysys("co -l #{BINDING}")
+
+print "Operator: "
+@operator = readline.chop
+unless @operator.index('@')
+ STDERR.puts "ERROR: No @ found"
+ exit 1
+end
+
+print "FPR Line: "
+@fprline = readline.chop
+(@nickname, @fpr) = parse_fprline(@fprline)
+
+binding = File.new(BINDING, "r+")
+binding.readlines.each do |line|
+ line.chop!
+ next if line[0..0] == "#"
+ (n,f) = parse_fprline(line)
+ if (n == @nickname)
+ STDERR.puts
+ STDERR.puts "ERROR: Nickname #{n} already exists in #{BINDING} (fpr: #{f})"
+ exit 1
+ end
+ if (f == @fpr)
+ STDERR.puts
+ STDERR.puts "ERROR: Fpr #{f} already exists in #{BINDING} (nickname: #{n})"
+ exit 1
+ end
+end
+
+puts
+puts '| # ' + @operator
+puts '| ' + @fprline
+puts
+
+binding.puts '# '+@operator
+binding.puts @fprline
+binding.close
+
+puts "Committing #{BINDING}..."
+mysys("ci -u -m'Add #{@nickname}' #{BINDING}")
diff --git a/contrib/checkSpace.pl b/contrib/checkSpace.pl
index 30c243e09f..aff96861a9 100755
--- a/contrib/checkSpace.pl
+++ b/contrib/checkSpace.pl
@@ -11,23 +11,23 @@ for $fn (@ARGV) {
$lastnil = 0;
$incomment = 0;
while (<F>) {
- ## Warn about windows-style newlines.
+ ## Warn about windows-style newlines.
if (/\r/) {
print " CR:$fn:$.\n";
}
- ## Warn about tabs.
+ ## Warn about tabs.
if (/\t/) {
print " TAB:$fn:$.\n";
}
- ## Warn about trailing whitespace.
+ ## Warn about trailing whitespace.
if (/ +$/) {
print "Space\@EOL:$fn:$.\n";
}
- ## Warn about control keywords without following space.
- if ($C && /\s(?:if|while|for|switch)\(/) {
- print " KW(:$fn:$.\n";
- }
- ## Warn about multiple empty lines.
+ ## Warn about control keywords without following space.
+ if ($C && /\s(?:if|while|for|switch)\(/) {
+ print " KW(:$fn:$.\n";
+ }
+ ## Warn about multiple empty lines.
if ($lastnil && /^$/) {
print " DoubleNL:$fn:$.\n";
} elsif (/^$/) {
@@ -35,15 +35,15 @@ for $fn (@ARGV) {
} else {
$lastnil = 0;
}
- ## Terminals are still 80 columns wide in my world. I refuse to
- ## accept double-line lines.
- if (/^.{80}/) {
- print " Wide:$fn:$.\n";
- }
-
- ### Juju to skip over comments and strings, since the tests
- ### we're about to do are okay there.
- if ($C) {
+ ## Terminals are still 80 columns wide in my world. I refuse to
+ ## accept double-line lines. Except, of course, svn Id tags
+ ## can make us go long.
+ if (/^.{80}/ && !/\$Id: /) {
+ print " Wide:$fn:$.\n";
+ }
+ ### Juju to skip over comments and strings, since the tests
+ ### we're about to do are okay there.
+ if ($C) {
if ($incomment) {
if (m!\*/!) {
s!.*?\*/!!;
@@ -83,30 +83,30 @@ for $fn (@ARGV) {
if (/(\w+)\s\(/) {
if ($1 ne "if" and $1 ne "while" and $1 ne "for" and
$1 ne "switch" and $1 ne "return" and $1 ne "int" and
+ $1 ne "elsif" and
$1 ne "void" and $1 ne "__attribute__") {
print " fn ():$fn:$.\n";
}
}
- ## Warn about functions not declared at start of line.
- if ($in_func_head ||
- ($fn !~ /\.h$/ && /^[a-zA-Z0-9_]/ &&
- ! /^(?:static )?(?:typedef|struct|union)[^\(]*$/ &&
- ! /= *\{$/ && ! /;$/)) {
-
- if (/.\{$/){
- print "fn() {:$fn:$.\n";
- $in_func_head = 0;
- } elsif (/^\S[^\(]* +\**[a-zA-Z0-9_]+\(/) {
- $in_func_head = -1; # started with tp fn
- } elsif (/;$/) {
- $in_func_head = 0;
- } elsif (/\{/) {
- if ($in_func_head == -1) {
- print "tp fn():$fn:$.\n";
- }
- $in_func_head = 0;
- }
- }
+ ## Warn about functions not declared at start of line.
+ if ($in_func_head ||
+ ($fn !~ /\.h$/ && /^[a-zA-Z0-9_]/ &&
+ ! /^(?:const |static )*(?:typedef|struct|union)[^\(]*$/ &&
+ ! /= *\{$/ && ! /;$/)) {
+ if (/.\{$/){
+ print "fn() {:$fn:$.\n";
+ $in_func_head = 0;
+ } elsif (/^\S[^\(]* +\**[a-zA-Z0-9_]+\(/) {
+ $in_func_head = -1; # started with tp fn
+ } elsif (/;$/) {
+ $in_func_head = 0;
+ } elsif (/\{/) {
+ if ($in_func_head == -1) {
+ print "tp fn():$fn:$.\n";
+ }
+ $in_func_head = 0;
+ }
+ }
}
}
if (! $lastnil) {
@@ -114,3 +114,4 @@ for $fn (@ARGV) {
}
close(F);
}
+
diff --git a/contrib/cross.sh b/contrib/cross.sh
new file mode 100644
index 0000000000..890e4496de
--- /dev/null
+++ b/contrib/cross.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+# $Id$
+# Copyright 2006 Michael Mohr with modifications by Roger Dingledine
+# See LICENSE for licensing information.
+
+#######################################################################
+# Tor-cross: a tool to help cross-compile Tor
+#
+# The purpose of a cross-compiler is to produce an executable for
+# one system (CPU) on another. This is useful, for example, when
+# the target system does not have a native compiler available.
+# You might, for example, wish to cross-compile a program on your
+# host (the computer you're working on now) for a target such as
+# a router or handheld computer.
+#
+# A number of environment variables must be set in order for this
+# script to work:
+# $PREFIX, $CROSSPATH, $ARCH_PREFIX, $HOST,
+# and (optionally) $BUILD
+# Please run the script for a description of each one. If automated
+# builds are desired, the above variables can be exported at the top
+# of this script.
+#
+# Recent releases of Tor include test programs in configure. Normally
+# this is a good thing, since it catches a number of problems.
+# However, this also presents a problem when cross compiling, since
+# you can't run binary images for the target system on the host.
+#
+# Tor-cross assumes that you know what you're doing and removes a
+# number of checks known to cause problems with this process.
+# Note that this does not guarantee that the program will run or
+# even compile; it simply allows configure to generate the Makefiles.
+#
+# Stripping the binaries should almost always be done for an
+# embedded environment where space is at an exacting premium.
+# However, the default is NOT to strip them since they are useful for
+# debugging. If you do not plan to do any debugging and you
+# don't care about the debugging symbols, set $STRIP to "yes" before
+# running this script.
+#
+# Tor-cross was written by Michael Mohr. He can be contacted at
+# m(dot)mohr(at)laposte(dot)net. Comments are appreciated, but
+# flames go to /dev/null.
+#
+# The target with which this script is tested is little-endian
+# MIPS Linux, built on an Athlon-based Linux desktop.
+#
+#######################################################################
+
+# disable the platform-specific tests in configure
+export CROSS_COMPILE=yes
+
+if [ ! -f configure ]
+then
+ echo "Please run this script from the root of the Tor distribution."
+ exit -1
+fi
+
+if [ -z $PREFIX ]
+then
+ echo "You must define \$PREFIX since you are cross-compiling."
+ echo "Select a non-system location (i.e. /tmp/tor-cross):"
+ echo " export PREFIX=/tmp/tor-cross"
+ exit -1
+fi
+
+if [ -z $CROSSPATH ]
+then
+ echo "You must define the location of your cross-compiler's"
+ echo "directory using \$CROSSPATH; for example,"
+ echo " export CROSSPATH=/opt/cross/staging_dir_mipsel/bin"
+ exit -1
+fi
+
+if [ -z $ARCH_PREFIX ]
+then
+ echo "You must define \$ARCH_PREFIX to continue. For example,"
+ echo "if you normally cross-compile applications using"
+ echo "mipsel-linux-uclibc-gcc, you would set \$ARCH_PREFIX like so:"
+ echo " export ARCH_PREFIX=mipsel-linux-uclibc-"
+ exit -1
+fi
+
+if [ -z $HOST ]
+then
+ echo "You must specify a target processor with \$HOST; for example:"
+ echo " export HOST=mipsel-unknown-elf"
+ exit -1
+fi
+
+if [ -z $BUILD ]
+then
+ echo "You should specify the host machine's type with \$BUILD; for example:"
+ echo " export BUILD=i686-pc-linux-gnu"
+ echo "If you wish to let configure autodetect the host, set \$BUILD to 'auto':"
+ echo " export BUILD=auto"
+ exit -1
+fi
+
+# clean up any existing object files
+if [ -f src/or/tor ]
+then
+ make clean
+fi
+
+# Set up the build environment and try to run configure
+export PATH=$PATH:$CROSSPATH
+export RANLIB=${ARCH_PREFIX}ranlib
+export CC=${ARCH_PREFIX}gcc
+
+if [ $BUILD == "auto" ]
+then
+ ./configure \
+ --prefix=$PREFIX \
+ --host=$HOST
+else
+ ./configure \
+ --prefix=$PREFIX \
+ --host=$HOST \
+ --build=$BUILD
+fi
+
+# has a problem occurred?
+if [ $? -ne 0 ]
+then
+ echo ""
+ echo "A problem has been detected with configure."
+ echo "Please check the output above and rerun cross.sh"
+ echo ""
+ exit -1
+fi
+
+# Now we're cookin'
+
+make
+
+# if $STRIP has length (i.e. STRIP=yes), strip the binaries
+if [ ! -z $STRIP ]
+then
+${ARCH_PREFIX}strip \
+ src/or/tor \
+ src/or/test \
+ src/tools/tor-resolve
+fi
+
+echo ""
+echo "Tor should be compiled at this point. Now run 'make install' to"
+echo "install to $PREFIX"
+echo ""
+
diff --git a/contrib/exitlist b/contrib/exitlist
index 96be25a7fa..de26dab89c 100755
--- a/contrib/exitlist
+++ b/contrib/exitlist
@@ -19,6 +19,10 @@
If you're using Tor 0.1.1.18-rc or later, you should look at
the "FetchUselessDescriptors" config option in the man page.
+ Note that this script won't give you a perfect list of IP addresses
+ that might connect to you using Tor, since some Tor servers might exit
+ from other addresses than the one they publish.
+
"""
#
@@ -196,6 +200,13 @@ class Server:
self.ip = ip
self.policy = policy
+def uniq_sort(lst):
+ d = {}
+ for item in lst: d[item] = 1
+ lst = d.keys()
+ lst.sort()
+ return lst
+
def run():
servers = []
policy = []
@@ -233,12 +244,13 @@ def run():
else:
printlist = accepters
+ ents = []
if VERBOSE:
- for s in printlist:
- print "%s\t%s"%(s.ip,s.name)
+ ents = uniq_sort([ "%s\t%s"%(s.ip,s.name) for s in printlist ])
else:
- for s in printlist:
- print s.ip
+ ents = uniq_sort([ s.ip for s in printlist ])
+ for e in ents:
+ print e
def _test():
import doctest, exitparse
diff --git a/contrib/id_to_fp.c b/contrib/id_to_fp.c
new file mode 100644
index 0000000000..e6f9f46279
--- /dev/null
+++ b/contrib/id_to_fp.c
@@ -0,0 +1,64 @@
+/* Copyright 2006 Nick Mathewson; see LICENSE for licensing information */
+/* $Id$ */
+
+/* id_to_fp.c : Helper for directory authority ops. When somebody sends us
+ * a private key, this utility converts the private key into a fingerprint
+ * so you can de-list that fingerprint.
+ */
+
+#include <openssl/rsa.h>
+#include <openssl/bio.h>
+#include <openssl/sha.h>
+#include <openssl/pem.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define die(s) do { fprintf(stderr, "%s\n", s); goto err; } while (0)
+
+int
+main(int argc, char **argv)
+{
+ BIO *b = NULL;
+ RSA *key = NULL;
+ unsigned char *buf = NULL, *bufp;
+ int len, i;
+ unsigned char digest[20];
+ int status = 1;
+
+ if (argc != 2)
+ die("I want a filename");
+ if (!(b = BIO_new_file(argv[1], "r")))
+ die("couldn't open file");
+
+ if (!(key = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL)))
+ die("couldn't parse key");
+
+ len = i2d_RSAPublicKey(key, NULL);
+ if (len < 0)
+ die("Bizarre key");
+ bufp = buf = malloc(len+1);
+ if (!buf)
+ die("Out of memory");
+ len = i2d_RSAPublicKey(key, &bufp);
+ if (len < 0)
+ die("Bizarre key");
+
+ SHA1(buf, len, digest);
+ for (i=0; i < 20; i += 2) {
+ printf("%02X%02X ", (int)digest[i], (int)digest[i+1]);
+ }
+ printf("\n");
+
+ status = 0;
+
+err:
+ if (buf)
+ free(buf);
+ if (key)
+ RSA_free(key);
+ if (b)
+ BIO_free(b);
+ return status;
+}
+
diff --git a/contrib/osx/Makefile.am b/contrib/osx/Makefile.am
index f26148593f..3fa7047ee1 100644
--- a/contrib/osx/Makefile.am
+++ b/contrib/osx/Makefile.am
@@ -6,4 +6,5 @@ EXTRA_DIST = PrivoxyConfDesc.plist PrivoxyConfInfo.plist \
TorInfo.plist.in TorStartupDesc.plist.in TorStartupInfo.plist \
package.sh privoxy.config TorPostflight addsysuser \
Tor_Uninstaller.applescript uninstall_tor_bundle.sh \
- package_list.txt tor_logo.gif
+ package_list.txt tor_logo.gif Tor_Uninstaller.app.tar.gz \
+ TorPreFlight
diff --git a/contrib/osx/TorPostflight b/contrib/osx/TorPostflight
index cd49924947..8c8c32aa4f 100644
--- a/contrib/osx/TorPostflight
+++ b/contrib/osx/TorPostflight
@@ -37,7 +37,7 @@ chown $TORUSER $LOGDIR
chgrp daemon $LOGDIR
chmod 700 $LOGDIR
-# Create the configuration file only if there wan't one already.
+# Create the configuration file only if there wasn't one already.
if [ ! -f $TARGET/torrc ]; then
cp $TARGET/torrc.sample $TARGET/torrc
fi
@@ -63,11 +63,6 @@ if [ ! -e /var/log/tor -o -L /var/log/tor ]; then
ln -sf $LOGDIR tor
fi
-if [ -d /Library/StartupItems/Tor ]; then
- rm -f /Library/StartupItems/Tor/Tor.loc
- echo "$TARGET" > /Library/StartupItems/Tor/Tor.loc
-fi
-
if [ -d /Library/StartupItems/Privoxy ]; then
find /Library/StartupItems/Privoxy -print0 | xargs -0 chown root:wheel
fi
@@ -83,6 +78,14 @@ if [ -f $PACKAGE_PATH/Contents/Resources/Tor_Uninstaller.applescript ]; then
chmod 755 $TARGET/Tor_Uninstaller.applescript
fi
+if [ -f $PACKAGE_PATH/Contents/Resources/Tor_Uninstaller.app.tar.gz ]; then
+ cp $PACKAGE_PATH/Contents/Resources/Tor_Uninstaller.app.tar.gz $TARGET/Tor_Uninstaller.app.tar.gz
+ cd $TARGET && tar zxf Tor_Uninstaller.app.tar.gz
+ chmod -R 755 $TARGET/Tor_Uninstaller.app
+ chown -R _tor:_tor Tor_Uninstaller.app
+ rm $TARGET/Tor_Uninstaller.app.tar.gz
+fi
+
if [ -f $PACKAGE_PATH/Contents/Resources/uninstall_tor_bundle.sh ]; then
cp $PACKAGE_PATH/Contents/Resources/uninstall_tor_bundle.sh $TARGET/uninstall_tor_bundle.sh
chmod 755 $TARGET/uninstall_tor_bundle.sh
@@ -91,3 +94,15 @@ fi
if [ -f $PACKAGE_PATH/Contents/Resources/package_list.txt ]; then
cp $PACKAGE_PATH/Contents/Resources/package_list.txt $TARGET/package_list.txt
fi
+
+# If the pre-install script did it's thing, it should have saved the
+# config and server keys; put these back and clean up
+if [ -f /tmp/TorSavedMe.tar.gz ]; then
+ tar zxf /tmp/TorSavedMe.tar.gz -C /
+ rm /tmp/TorSavedMe.tar.gz
+fi
+
+if [ -d /Library/StartupItems/Tor ]; then
+ rm -f /Library/StartupItems/Tor/Tor.loc
+ echo "$TARGET" > /Library/StartupItems/Tor/Tor.loc
+fi
diff --git a/contrib/osx/TorPreFlight b/contrib/osx/TorPreFlight
new file mode 100644
index 0000000000..b147264861
--- /dev/null
+++ b/contrib/osx/TorPreFlight
@@ -0,0 +1,29 @@
+#!/bin/sh
+# TorPreFlight is invoked before the install begins
+
+# Figure out where Tor is installed
+if [ -f /Library/StartupItems/Tor/Tor.loc ]; then
+ TORPATH=`cat /Library/StartupItems/Tor/Tor.loc`
+else
+ TORPATH="/Library/Tor/"
+fi
+
+if [ -f /Library/StartupItems/Privoxy/Privoxy.loc ]; then
+ PRIVOXYPATH=`cat /Library/StartupItems/Privoxy/Privoxy.loc`
+else
+ PRIVOXYPATH="/Library/Privoxy/"
+fi
+
+# Backup all of Tor, just in case
+if [ -d $TORPATH ]; then
+ tar zcf /tmp/TorSavedMe.tar.gz $TORPATH/var/lib/tor $TORPATH/torrc $PRIVOXYPATH/config $PRIVOXYPATH/user.action
+fi
+
+# Remove Tor and everything to do with it
+if [ -f $TORPATH/uninstall_tor_bundle.sh ]; then
+ $TORPATH/uninstall_tor_bundle.sh
+else
+ $PACKAGE_PATH/Contents/Resources/uninstall_tor_bundle.sh
+fi
+
+# This is complete, we have a fresh system on which to install Tor
diff --git a/contrib/osx/Tor_Uninstaller.app.tar.gz b/contrib/osx/Tor_Uninstaller.app.tar.gz
new file mode 100644
index 0000000000..add053a1fe
--- /dev/null
+++ b/contrib/osx/Tor_Uninstaller.app.tar.gz
Binary files differ
diff --git a/contrib/osx/Tor_Uninstaller.applescript b/contrib/osx/Tor_Uninstaller.applescript
index 79ac46f9aa..246e265bbe 100644
--- a/contrib/osx/Tor_Uninstaller.applescript
+++ b/contrib/osx/Tor_Uninstaller.applescript
@@ -35,31 +35,34 @@
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- ===============================================================================
--- Validate & find disk paths
-set boot_disk to (path to startup disk) as string
-set default_tor_path to boot_disk & "Library:Tor"
-set default_privoxy_path to boot_disk & "Library:Privoxy"
-set default_tor_startup_path to boot_disk & "Library:StartupItems:Tor"
-set default_privoxy_startup_path to boot_disk & "Library:StartupItems:Privoxy"
-set shell_script to default_tor_path & ":uninstall_tor_bundle.sh"
-set doomed_path_list to {default_tor_path, default_privoxy_path, default_tor_startup_path, default_privoxy_startup_path}
-
--- Display what we're removing and ask for validation
--- this is the simplest way to do this
-set remove_me to display dialog "Welcome to the Tor + Privoxy Uninstaller. This program will remove:" & return & default_tor_path & return & default_privoxy_path & return & default_tor_startup_path & return & default_privoxy_startup_path & return & return & "If this does not look right, choose Yes. Otherwise, choose No." buttons {"Yes", "No"} default button "No"
-
--- Run a shell script to do all the unix work since applescript can't see it at all
-if button returned of result is "Yes" then
- try
- do shell script (shell_script) with administrator privileges
- on error
- display dialog "Too many errors, quitting." buttons {"Quit"} default button "Quit" with icon stop giving up after 5
- quit
- end try
- -- So Long and Thanks for all the Fish!
- display dialog "Thank you for using tor!" buttons {"Ok"} giving up after 5
-else
- display dialog "Thank you for your continued use of Tor & Privoxy" buttons {"You're welcome."}
-end if
-
+on run
+
+ -- Validate & find disk paths
+ set boot_disk to (path to startup disk) as string
+ set default_tor_path to boot_disk & "Library:Tor"
+ set default_privoxy_path to boot_disk & "Library:Privoxy"
+ set default_tor_startup_path to boot_disk & "Library:StartupItems:Tor"
+ set default_privoxy_startup_path to boot_disk & "Library:StartupItems:Privoxy"
+ set shell_script to default_tor_path & ":uninstall_tor_bundle.sh"
+ set doomed_path_list to {default_tor_path, default_privoxy_path, default_tor_startup_path, default_privoxy_startup_path}
+
+ -- Display what we're removing and ask for validation
+ -- this is the simplest way to do this
+ set remove_me to display dialog "Welcome to the Tor + Privoxy Uninstaller. This program will remove:" & return & return & POSIX path of default_tor_path & return & POSIX path of default_privoxy_path & return & POSIX path of default_tor_startup_path & return & POSIX path of default_privoxy_startup_path & return & return & "If this looks correct, choose Yes. Otherwise, choose No." buttons {"Yes", "No"} default button "No"
+
+ -- Run a shell script to do all the unix work since applescript can't see it at all
+ if button returned of result is "Yes" then
+ try
+ do shell script (POSIX path of shell_script) with administrator privileges
+ on error
+ display dialog "Too many errors, quitting." buttons {"Quit"} default button "Quit" with icon stop giving up after 3
+ quit
+ end try
+ -- So Long and Thanks for all the Fish!
+ display dialog "Thank you for using tor!" buttons {"Ok"} giving up after 3
+ else
+ display dialog "Thank you for your continued use of Tor & Privoxy" buttons {"You're welcome."} giving up after 3
+ end if
+
+end run
-- We're done \ No newline at end of file
diff --git a/contrib/osx/package.sh b/contrib/osx/package.sh
index d63c3efab4..85b0e43660 100644
--- a/contrib/osx/package.sh
+++ b/contrib/osx/package.sh
@@ -26,7 +26,7 @@ PRIVOXY_PKG_ZIP=~/tmp/privoxyosx_setup_3.0.3.zip
# man packagemaker
# Make sure VERSION is set, so we don't name the package
-# "Tor--$OS-$ARCH-Bundle.dmg"
+# "Tor--$OS-Bundle.dmg"
if [ "XX$VERSION" = 'XX' ]; then
echo "VERSION not set."
exit 1
@@ -83,6 +83,7 @@ cp contrib/osx/ReadMe.rtf $BUILD_DIR/tor_resources
#cp contrib/osx/License.rtf $BUILD_DIR/tor_resources
chmod 755 contrib/osx/TorPostflight
cp contrib/osx/TorPostflight $BUILD_DIR/tor_resources/postflight
+cp contrib/osx/TorPreFlight $BUILD_DIR/tor_resources/preflight
cp contrib/osx/addsysuser $BUILD_DIR/tor_resources/addsysuser
cp contrib/osx/Tor_Uninstaller.applescript $BUILD_DIR/tor_resources/Tor_Uninstaller.applescript
cp contrib/osx/Tor_Uninstaller.app.tar.gz $BUILD_DIR/tor_resources/Tor_Uninstaller.app.tar.gz
diff --git a/contrib/osx/uninstall_tor_bundle.sh b/contrib/osx/uninstall_tor_bundle.sh
index 9d69d09e54..18b346005c 100755
--- a/contrib/osx/uninstall_tor_bundle.sh
+++ b/contrib/osx/uninstall_tor_bundle.sh
@@ -134,7 +134,7 @@ niutil -destroy . /users/$TOR_USER
## clean up
echo ". Cleaning up"
rm -rf $TEMP_BOM_CONTENTS
-rm -rf /Library/Privoxy/ /Library/StartupItems/Tor/ /Library/StartupItems/Privoxy/ /Library/Tor/
+rm -rf /Library/Privoxy/ /Library/StartupItems/Privoxy/ /Library/Tor/ /Library/StartupItems/Tor/
echo ". Finished"
diff --git a/contrib/package_nsis-mingw.sh b/contrib/package_nsis-mingw.sh
new file mode 100644
index 0000000000..661bdbb403
--- /dev/null
+++ b/contrib/package_nsis-mingw.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Script to package a Tor installer on win32. This script assumes that
+# you have already built Tor, that you are running msys/mingw, and that
+# you know what you are doing.
+
+# Start in the tor source directory after you've compiled tor.exe
+
+rm -rf win_tmp
+mkdir win_tmp
+mkdir win_tmp/bin
+mkdir win_tmp/contrib
+mkdir win_tmp/doc
+mkdir win_tmp/doc/design-paper
+mkdir win_tmp/doc/contrib
+mkdir win_tmp/src
+mkdir win_tmp/src/config
+mkdir win_tmp/tmp
+
+cp src/or/tor.exe win_tmp/bin/tor.exe
+cp src/tools/tor_resolve.exe win_tmp/bin
+cp /usr/local/ssl/lib/libcrypto.a win_tmp/bin
+cp /usr/local/ssl/lib/libssl.a win_tmp/bin
+
+# There is no man2html in mingw.
+# Maybe we should add this into make dist instead.
+man2html doc/tor.1.in > win_tmp/tmp/tor-reference.html
+man2html doc/tor-resolve.1 > win_tmp/tmp/tor-resolve.html
+
+clean_newlines() {
+ perl -pe 's/^\n$/\r\n/mg; s/([^\r])\n$/\1\r\n/mg;' $1 >$2
+}
+
+clean_localstatedir() {
+ perl -pe 's/^\n$/\r\n/mg; s/([^\r])\n$/\1\r\n/mg; s{\@LOCALSTATEDIR\@/(lib|log)/tor/}{C:\\Documents and Settings\\Application Data\\Tor\\}' $1 >$2
+}
+
+for fn in tor-spec.txt HACKING rend-spec.txt control-spec.txt \
+ tor-doc.html tor-doc.css version-spec.txt; do
+ clean_newlines doc/$fn win_tmp/doc/$fn
+done
+
+cp doc/design-paper/tor-design.pdf win_tmp/doc/design-paper/tor-design.pdf
+
+for fn in tor-reference.html tor-resolve.html; do \
+ clean_newlines win_tmp/tmp/$fn win_tmp/doc/$fn
+done
+
+for fn in README AUTHORS ChangeLog LICENSE; do \
+ clean_newlines $fn win_tmp/$fn
+done
+
+clean_localstatedir src/config/torrc.sample.in win_tmp/src/config/torrc.sample
+
+cp contrib/tor.nsi win_tmp/contrib
+
+cd win_tmp/contrib
+C:\Program Files\NSIS\makensis.exe tor.nsi
+mv tor-*.exe ../..
diff --git a/contrib/tor-mingw.nsi.in b/contrib/tor-mingw.nsi.in
new file mode 100644
index 0000000000..d83aa5ca5e
--- /dev/null
+++ b/contrib/tor-mingw.nsi.in
@@ -0,0 +1,212 @@
+;tor.nsi - A basic win32 installer for Tor
+; Originally written by J Doe.
+; See LICENSE for licencing information
+;-----------------------------------------
+;
+; How to make an installer:
+; Step 0. If you are a Tor maintainer, make sure that tor.nsi and
+; src/win32/orconfig.h all have the correct version number.
+; Step 1. Download and install OpenSSL. Make sure that the OpenSSL
+; version listed below matches the one you downloaded.
+; Step 2. Download and install NSIS (http://nsis.sourceforge.net)
+; Step 3. Make a directory under the main tor directory called "bin".
+; Step 4. Copy ssleay32.dll and libeay32.dll from OpenSSL into "bin".
+; Step 5. Run man2html on tor.1.in; call the result tor-reference.html
+; Run man2html on tor-resolve.1; call the result tor-resolve.html
+; Step 6. Copy torrc.sample.in to torrc.sample.
+; Step 7. Build tor.exe and tor_resolve.exe; save the result into bin.
+; Step 8. cd into contrib and run "makensis tor.nsi".
+;
+; Problems:
+; - Copying torrc.sample.in to torrc.sample and tor.1.in (implicitly)
+; to tor.1 is a Bad Thing, and leaves us with @autoconf@ vars in the final
+; result.
+; - Building Tor requires too much windows C clue.
+; - We should have actual makefiles for VC that do the right thing.
+; - I need to learn more NSIS juju to solve these:
+; - There should be a batteries-included installer that comes with
+; privoxy too. (Check privoxy license on this; be sure to include
+; all privoxy documents.)
+; - The filename should probably have a revision number.
+
+!include "MUI.nsh"
+
+!define VERSION "0.1.2.1-alpha-dev"
+!define INSTALLER "tor-${VERSION}-win32.exe"
+!define WEBSITE "http://tor.eff.org/"
+
+!define LICENSE "..\LICENSE"
+;BIN is where it expects to find tor.exe, tor_resolve.exe, libcrypto.a
+;and libssl.a
+!define BIN "..\bin"
+
+SetCompressor lzma
+;SetCompressor zlib
+OutFile ${INSTALLER}
+InstallDir $PROGRAMFILES\Tor
+SetOverWrite ifnewer
+
+Name "Tor"
+Caption "Tor ${VERSION} Setup"
+BrandingText "The Onion Router"
+CRCCheck on
+
+;Use upx on the installer header to shrink the size.
+!packhdr header.dat "upx --best header.dat"
+
+!define MUI_WELCOMEPAGE_TITLE "Welcome to the Tor ${VERSION} Setup Wizard"
+!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Tor ${VERSION}.\r\n\r\nIf you have previously installed Tor and it is currently running, please exit Tor first before continuing this installation.\r\n\r\n$_CLICK"
+!define MUI_ABORTWARNING
+!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\win-install.ico"
+!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\win-uninstall.ico"
+!define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\win.bmp"
+!define MUI_HEADERIMAGE
+!define MUI_FINISHPAGE_RUN "$INSTDIR\tor.exe"
+!define MUI_FINISHPAGE_LINK "Visit the Tor website for the latest updates."
+!define MUI_FINISHPAGE_LINK_LOCATION ${WEBSITE}
+
+!insertmacro MUI_PAGE_WELCOME
+; There's no point in having a clickthrough license: Our license adds
+; certain rights, but doesn't remove them.
+; !insertmacro MUI_PAGE_LICENSE "${LICENSE}"
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+!insertmacro MUI_LANGUAGE "English"
+
+Var configdir
+Var configfile
+
+;Sections
+;--------
+
+Section "Tor" Tor
+;Files that have to be installed for tor to run and that the user
+;cannot choose not to install
+ SectionIn RO
+ SetOutPath $INSTDIR
+ File "${BIN}\tor.exe"
+ File "${BIN}\tor_resolve.exe"
+ WriteIniStr "$INSTDIR\Tor Website.url" "InternetShortcut" "URL" ${WEBSITE}
+
+ StrCpy $configfile "torrc"
+ StrCpy $configdir $APPDATA\Tor
+; ;If $APPDATA isn't valid here (Early win95 releases with no updated
+; ; shfolder.dll) then we put it in the program directory instead.
+; StrCmp $APPDATA "" "" +2
+; StrCpy $configdir $INSTDIR
+ SetOutPath $configdir
+ ;If there's already a torrc config file, ask if they want to
+ ;overwrite it with the new one.
+ IfFileExists "$configdir\torrc" "" endiftorrc
+ MessageBox MB_ICONQUESTION|MB_YESNO "You already have a Tor config file.$\r$\nDo you want to overwrite it with the default sample config file?" IDNO yesreplace
+ Delete $configdir\torrc
+ Goto endiftorrc
+ yesreplace:
+ StrCpy $configfile "torrc.sample"
+ endiftorrc:
+ File /oname=$configfile "..\src\config\torrc.sample"
+SectionEnd
+
+Section "OpenSSL 0.9.8c" OpenSSL
+ SetOutPath $INSTDIR
+ File "${BIN}\libcrypto.a"
+ File "${BIN}\libssl.a"
+SectionEnd
+
+Section "Documents" Docs
+ SetOutPath "$INSTDIR\Documents"
+ ;File "..\doc\FAQ"
+ File "..\doc\HACKING"
+ File "..\doc\control-spec.txt"
+ File "..\doc\dir-spec.txt"
+ File "..\doc\rend-spec.txt"
+ File "..\doc\socks-extensions.txt"
+ File "..\doc\version-spec.txt"
+ ;
+ ; WEBSITE-FILES-HERE
+ ;
+ File "..\doc\tor-resolve.html"
+ File "..\doc\tor-reference.html"
+ ;
+ File "..\doc\design-paper\tor-design.pdf"
+ ;
+ File "..\README"
+ File "..\AUTHORS"
+ File "..\ChangeLog"
+ File "..\LICENSE"
+SectionEnd
+
+SubSection /e "Shortcuts" Shortcuts
+
+Section "Start Menu" StartMenu
+ SetOutPath $INSTDIR
+ IfFileExists "$SMPROGRAMS\Tor\*.*" "" +2
+ RMDir /r "$SMPROGRAMS\Tor"
+ CreateDirectory "$SMPROGRAMS\Tor"
+ CreateShortCut "$SMPROGRAMS\Tor\Tor.lnk" "$INSTDIR\tor.exe"
+ CreateShortCut "$SMPROGRAMS\Tor\Torrc.lnk" "Notepad.exe" "$configdir\torrc"
+ CreateShortCut "$SMPROGRAMS\Tor\Tor Website.lnk" "$INSTDIR\Tor Website.url"
+ CreateShortCut "$SMPROGRAMS\Tor\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
+ IfFileExists "$INSTDIR\Documents\*.*" "" endifdocs
+ CreateDirectory "$SMPROGRAMS\Tor\Documents"
+ CreateShortCut "$SMPROGRAMS\Tor\Documents\Tor Manual.lnk" "$INSTDIR\Documents\tor-doc.html"
+ CreateShortCut "$SMPROGRAMS\Tor\Documents\Tor Documentation.lnk" "$INSTDIR\Documents"
+ CreateShortCut "$SMPROGRAMS\Tor\Documents\Tor Specification.lnk" "$INSTDIR\Documents\tor-spec.txt"
+ endifdocs:
+SectionEnd
+
+Section "Desktop" Desktop
+ SetOutPath $INSTDIR
+ CreateShortCut "$DESKTOP\Tor.lnk" "$INSTDIR\tor.exe"
+SectionEnd
+
+Section /o "Run at startup" Startup
+ SetOutPath $INSTDIR
+ CreateShortCut "$SMSTARTUP\Tor.lnk" "$INSTDIR\tor.exe" "" "" 0 SW_SHOWMINIMIZED
+SectionEnd
+
+SubSectionEnd
+
+Section "Uninstall"
+ Delete "$DESKTOP\Tor.lnk"
+ Delete "$INSTDIR\libcrypto.a"
+ Delete "$INSTDIR\libssl.a"
+ Delete "$INSTDIR\tor.exe"
+ Delete "$INSTDIR\tor_resolve.exe"
+ Delete "$INSTDIR\Tor Website.url"
+ Delete "$INSTDIR\torrc"
+ Delete "$INSTDIR\torrc.sample"
+ StrCmp $configdir $INSTDIR +2 ""
+ RMDir /r $configdir
+ Delete "$INSTDIR\Uninstall.exe"
+ RMDir /r "$INSTDIR\Documents"
+ RMDir $INSTDIR
+ RMDir /r "$SMPROGRAMS\Tor"
+ Delete "$SMSTARTUP\Tor.lnk"
+ DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Tor"
+SectionEnd
+
+Section -End
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+ ;The registry entries simply add the Tor uninstaller to the Windows
+ ;uninstall list.
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tor" "DisplayName" "Tor (remove only)"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tor" "UninstallString" '"$INSTDIR\Uninstall.exe"'
+SectionEnd
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${Tor} "The core executable and config files needed for Tor to run."
+ !insertmacro MUI_DESCRIPTION_TEXT ${OpenSSL} "OpenSSL libraries required by Tor."
+ !insertmacro MUI_DESCRIPTION_TEXT ${Docs} "Documentation about Tor."
+ !insertmacro MUI_DESCRIPTION_TEXT ${ShortCuts} "Shortcuts to easily start Tor"
+ !insertmacro MUI_DESCRIPTION_TEXT ${StartMenu} "Shortcuts to access Tor and it's documentation from the Start Menu"
+ !insertmacro MUI_DESCRIPTION_TEXT ${Desktop} "A shortcut to start Tor from the desktop"
+ !insertmacro MUI_DESCRIPTION_TEXT ${Startup} "Launches Tor automatically at startup in a minimized window"
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
diff --git a/contrib/tor-resolve.py b/contrib/tor-resolve.py
index dd44255bc1..919bc876cc 100755
--- a/contrib/tor-resolve.py
+++ b/contrib/tor-resolve.py
@@ -32,15 +32,16 @@ def socks5Hello():
def socks5ParseHello(response):
if response != "\x05\x00":
raise ValueError("Bizarre socks5 response")
-def socks5ResolveRequest(hostname):
+def socks5ResolveRequest(hostname, atype=0x03, command=0xF0):
version = 5
- command = 0xF0
rsv = 0
port = 0
- atype = 0x03
- reqheader = struct.pack("!BBBBB",version, command, rsv, atype, len(hostname))
+ reqheader = struct.pack("!BBBB",version, command, rsv, atype)
+ if atype == 0x03:
+ reqheader += struct.pack("!B", len(hostname))
portstr = struct.pack("!H",port)
return "%s%s%s"%(reqheader,hostname,portstr)
+
def socks5ParseResponse(r):
if len(r)<8:
return None
@@ -49,18 +50,30 @@ def socks5ParseResponse(r):
assert rsv==0
if reply != 0x00:
return "ERROR",reply
- assert atype in (0x01,0x04)
- expected_len = 4 + ({1:4,4:16}[atype]) + 2
- if len(r) < expected_len:
- return None
- elif len(r) > expected_len:
- raise ValueError("Overlong socks5 reply!")
- addr = r[4:-2]
- if atype == 0x01:
- return "%d.%d.%d.%d"%tuple(map(ord,addr))
+ assert atype in (0x01,0x03,0x04)
+ if atype != 0x03:
+ expected_len = 4 + ({1:4,4:16}[atype]) + 2
+ if len(r) < expected_len:
+ return None
+ elif len(r) > expected_len:
+ raise ValueError("Overlong socks5 reply!")
+ addr = r[4:-2]
+ if atype == 0x01:
+ return "%d.%d.%d.%d"%tuple(map(ord,addr))
+ else:
+ # not really the right way to format IPv6
+ return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
else:
- # not really the right way to format IPv6
- return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
+ hlen, = struct.unpack("!B", r[4])
+ expected_len = 5 + hlen + 2
+ if len(r) < expected_len:
+ return None
+ return r[5:-2]
+
+def socks5ResolvePTRRequest(hostname):
+ return socks5ResolveRequest(socket.inet_aton(hostname),
+ atype=1, command = 0xF1)
+
def parseHostAndPort(h):
host, port = "localhost", 9050
@@ -80,20 +93,23 @@ def parseHostAndPort(h):
return host, port
-def resolve(hostname, sockshost, socksport, socksver=4):
+def resolve(hostname, sockshost, socksport, socksver=4, reverse=0):
assert socksver in (4,5)
if socksver == 4:
fmt = socks4AResolveRequest
parse = socks4AParseResponse
- else:
+ elif not reverse:
fmt = socks5ResolveRequest
parse = socks5ParseResponse
+ else:
+ fmt = socks5ResolvePTRRequest
+ parse = socks5ParseResponse
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((sockshost,socksport))
if socksver == 5:
s.send(socks5Hello())
socks5ParseHello(s.recv(2))
- print len(fmt(hostname)), len(hostname)
s.send(fmt(hostname))
answer = s.recv(6)
result = parse(answer)
@@ -114,14 +130,25 @@ if __name__ == '__main__':
print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
sys.exit(0)
socksver = 4
- if sys.argv[1] in ("-4", "-5"):
- socksver = int(sys.argv[1][1])
- del sys.argv[1]
- if len(sys.argv) == 4:
- print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
+ reverse = 0
+ while sys.argv[1][0] == '-':
+ if sys.argv[1] in ("-4", "-5"):
+ socksver = int(sys.argv[1][1])
+ del sys.argv[1]
+ elif sys.argv[1] == '-x':
+ reverse = 1
+ del sys.argv[1]
+ elif sys.argv[1] == '--':
+ break
+
+ if len(sys.argv) >= 4:
+ print "Syntax: resolve.py [-x] [-4|-5] hostname [sockshost:socksport]"
sys.exit(0)
if len(sys.argv) == 3:
sh,sp = parseHostAndPort(sys.argv[2])
else:
sh,sp = parseHostAndPort("")
- resolve(sys.argv[1], sh, sp, socksver)
+
+ if reverse and socksver == 4:
+ socksver = 5
+ resolve(sys.argv[1], sh, sp, socksver, reverse)
diff --git a/contrib/tor.nsi.in b/contrib/tor.nsi.in
index 295a8ec21c..4fc8d34c1a 100644
--- a/contrib/tor.nsi.in
+++ b/contrib/tor.nsi.in
@@ -31,7 +31,7 @@
!include "MUI.nsh"
-!define VERSION "0.1.1.23"
+!define VERSION "0.1.2.1-alpha-dev"
!define INSTALLER "tor-${VERSION}-win32.exe"
!define WEBSITE "http://tor.eff.org/"
diff --git a/contrib/torify.in b/contrib/torify.in
index f9162a19fe..05645fd07c 100755
--- a/contrib/torify.in
+++ b/contrib/torify.in
@@ -1,7 +1,45 @@
#! /bin/sh
+
# Wrapper script for use of the tsocks(8) transparent socksification library
# See the tsocks(1) and torify(1) manpages.
-TSOCKS_CONF_FILE=@CONFDIR@/tor-tsocks.conf
+# Copyright (c) 2004, 2006 Peter Palfrader
+# Modified by Jacob Appelbaum <jacob@appelbaum.net> April 16th 2006
+# May be distributed under the same terms as Tor itself
+
+
+# Define and ensure we have tsocks
+# XXX: what if we don't have which?
+TSOCKS="`which tsocks`"
+if [ ! -x "$TSOCKS" ]
+then
+ echo "$0: Can't find tsocks in PATH. Perhaps you haven't installed it?" >&2
+ exit 1
+fi
+
+# Check for any argument list
+if [ "$#" = 0 ]
+then
+ echo "Usage: $0 <command> [<options>...]" >&2
+ exit 1
+fi
+if [ "$#" = 1 ] && ( [ "$1" = "-h" ] || [ "$1" = "--help" ] )
+then
+ echo "Usage: $0 <command> [<options>...]"
+ exit 0
+fi
+
+# Define our tsocks config file
+TSOCKS_CONF_FILE="@CONFDIR@/tor-tsocks.conf"
export TSOCKS_CONF_FILE
-exec tsocks "$@"
+
+# Check that we've got a tsocks config file
+if [ -r "$TSOCKS_CONF_FILE" ]
+then
+ exec tsocks "$@"
+ echo "$0: Failed to exec tsocks $@" >&2
+ exit 1
+else
+ echo "$0: Missing tsocks configuration file \"$TSOCKS_CONF_FILE\"." >&2
+ exit 1
+fi
diff --git a/debian/README.Debian b/debian/README.Debian
index 6c9730d6c3..cda020b9e7 100644
--- a/debian/README.Debian
+++ b/debian/README.Debian
@@ -1,13 +1,14 @@
This is the Debian package for Tor, The Onion Router.
-The following changes have been made to the Tor source to integrate it better
-into Debian:
+Some changes have been made to the Tor source to integrate it better into
+Debian. If Tor is started as the 'debian-tor' user, then:
- RunAsDaemon is enabled by default.
- PidFile is set to /var/run/tor/tor.pid. No default upstream.
- default logging goes to /var/log/tor/log instead of stdout.
- DataDirectory is set to /var/lib/tor by default. Uses $HOME/.tor upstream.
+If Tor is started as any other user it behaves just like upstream's.
--
-Peter Palfrader, Fri, 12 Nov 2004 18:08:54 +0100
+Peter Palfrader, Mon, 24 Jul 2006 05:20:30 +0200
diff --git a/debian/changelog b/debian/changelog
index c84079444e..615802d61b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,26 @@
+tor (0.1.2.1-alpha-XXXX) XXXerimental; urgency=low
+
+ * Minor update of debian/copyright [arma].
+
+ -- Peter Palfrader <weasel@debian.org> Sun, 3 Sep 2006 19:53:45 +0200
+
+tor (0.1.2.1-alpha-1) experimental; urgency=low
+
+ * Forward port 07_log_to_file_by_default.dpatch.
+ * Previously our defaults for DataDirectory, PidFile, RunAsDaemon, and
+ Log differed from upstreams. Now Tor behaves just like before (with
+ our own DataDirectory and all) only when run as the debian-tor user.
+ If invoked as any other user, Tor will behave just like the pristine
+ upstream version.
+ * Tell users about the init script when they try to run Tor as root.
+ Should we also do this when they try to run their Tor as any other
+ (non root, non debian-tor) user? - add 11_tor_as_root_more_helpful
+ * Use tor --verify-config before start and reload. Abort init script
+ with exit 1 if config does not verify.
+ * Change Standards-Version to 3.7.2. No changes required.
+
+ -- Peter Palfrader <weasel@debian.org> Tue, 29 Aug 2006 22:38:29 +0200
+
tor (0.1.1.23-XXXXXX) unstable; urgency=low
* Update debian/copyright:
diff --git a/debian/control b/debian/control
index 7599169fcc..7820f3c4a1 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Section: comm
Priority: optional
Maintainer: Peter Palfrader <weasel@debian.org>
Build-Depends: debhelper (>= 4.1.65), libssl-dev, dpatch, zlib1g-dev, libevent-dev (>= 1.1), tetex-bin, tetex-extra, transfig, gs, binutils (>= 2.14.90.0.7)
-Standards-Version: 3.6.2
+Standards-Version: 3.7.2
Package: tor
Architecture: any
diff --git a/debian/copyright b/debian/copyright
index 6ce5c8ab8c..f010a4580f 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -13,7 +13,7 @@ strlcat, strlcpy: Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
ht.h: Copyright (c) 2002, Christopher Clark, 2006 Nick Mathewson
Modifications for Debian: Copyright (c) 2004, 2005, 2006 Peter Palfrader
-The tor code is under this license:
+Tor is distributed under this license:
===============================================================================
Copyright (c) 2001-2004, Roger Dingledine
Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
diff --git a/debian/patches/00list b/debian/patches/00list
index cd17cd1dca..0c2be8424f 100644
--- a/debian/patches/00list
+++ b/debian/patches/00list
@@ -2,3 +2,4 @@
03_tor_manpage_in_section_8.dpatch
06_add_compile_time_defaults.dpatch
07_log_to_file_by_default.dpatch
+11_tor_as_root_more_helpful.dpatch
diff --git a/debian/patches/06_add_compile_time_defaults.dpatch b/debian/patches/06_add_compile_time_defaults.dpatch
index 81e6034d3c..8b4e494246 100755
--- a/debian/patches/06_add_compile_time_defaults.dpatch
+++ b/debian/patches/06_add_compile_time_defaults.dpatch
@@ -23,33 +23,97 @@ esac
exit 0
@DPATCH@
-diff -urNad tor-0.1.1.5/src/or/config.c /tmp/dpep.Xv4cHn/tor-0.1.1.5/src/or/config.c
---- tor-0.1.1.9/src/or/config.c 2005-10-17 03:54:24.917618642 +0200
-+++ /tmp/foo/tor/src/or/config.c 2005-10-17 03:57:52.040022393 +0200
-@@ -111,7 +111,7 @@
- VAR("ContactInfo", STRING, ContactInfo, NULL),
- VAR("ControlPort", UINT, ControlPort, "0"),
- VAR("CookieAuthentication",BOOL, CookieAuthentication, "0"),
-- VAR("DataDirectory", STRING, DataDirectory, NULL),
-+ VAR("DataDirectory", STRING, DataDirectory, "/var/lib/tor"),
- VAR("DebugLogFile", STRING, DebugLogFile, NULL),
- VAR("DirAllowPrivateAddresses",BOOL, DirAllowPrivateAddresses, NULL),
- VAR("DirBindAddress", LINELIST, DirBindAddress, NULL),
-@@ -162,7 +162,7 @@
- VAR("ORPort", UINT, ORPort, "0"),
- VAR("OutboundBindAddress", STRING, OutboundBindAddress, NULL),
- VAR("PathlenCoinWeight", DOUBLE, PathlenCoinWeight, "0.3"),
-- VAR("PidFile", STRING, PidFile, NULL),
-+ VAR("PidFile", STRING, PidFile, "/var/run/tor/tor.pid"),
- VAR("ProtocolWarnings", BOOL, ProtocolWarnings, "0"),
- VAR("ReachableAddresses", LINELIST, ReachableAddresses, NULL),
- VAR("RecommendedVersions", LINELIST, RecommendedVersions, NULL),
-@@ -174,7 +174,7 @@
- VAR("RendPostPeriod", INTERVAL, RendPostPeriod, "20 minutes"),
- VAR("RephistTrackTime", INTERVAL, RephistTrackTime, "24 hours"),
- OBSOLETE("RouterFile"),
-- VAR("RunAsDaemon", BOOL, RunAsDaemon, "0"),
-+ VAR("RunAsDaemon", BOOL, RunAsDaemon, "1"),
- VAR("RunTesting", BOOL, RunTesting, "0"),
- VAR("SafeLogging", BOOL, SafeLogging, "1"),
- VAR("ShutdownWaitLength", INTERVAL, ShutdownWaitLength, "30 seconds"),
+diff -urNad tor~/src/or/config.c tor/src/or/config.c
+--- tor~/src/or/config.c 2006-07-23 19:31:29.000000000 +0200
++++ tor/src/or/config.c 2006-07-24 05:13:19.924871985 +0200
+@@ -12,6 +12,7 @@
+ **/
+
+ #include "or.h"
++#include <pwd.h>
+ #ifdef MS_WINDOWS
+ #include <shlobj.h>
+ #endif
+@@ -396,6 +397,10 @@
+ static void check_libevent_version(const char *m, const char *v, int server);
+ #endif
+
++static int debian_running_as_debiantor();
++static int debian_config_fix_defaults();
++
++
+ /*static*/ or_options_t *options_new(void);
+
+ #define OR_OPTIONS_MAGIC 9090909
+@@ -2663,7 +2668,7 @@
+ int
+ options_init_from_torrc(int argc, char **argv)
+ {
+- or_options_t *oldoptions, *newoptions;
++ or_options_t *oldoptions, *newoptions = NULL;
+ config_line_t *cl;
+ char *cf=NULL, *fname=NULL, *errmsg=NULL;
+ int i, retval;
+@@ -2671,6 +2676,9 @@
+ static char **backup_argv;
+ static int backup_argc;
+
++ if (debian_config_fix_defaults() < 0)
++ goto err;
++
+ if (argv) { /* first time we're called. save commandline args */
+ backup_argv = argv;
+ backup_argc = argc;
+@@ -3948,3 +3956,52 @@
+ puts(routerparse_c_id);
+ }
+
++/* Checks whether we are running as the debian-tor user.
++ * Returns -1 on error, 1 if we are debian-tor, 0 if not */
++static int
++debian_running_as_debiantor()
++{
++ struct passwd *pw = NULL;
++ int uid;
++
++ uid = getuid();
++ pw = getpwuid(uid);
++ if (!pw) {
++ log(LOG_WARN, LD_GENERAL, "Could not get passwd information for %d.", uid);
++ return -1;
++ }
++ assert(pw->pw_name);
++ if (strcmp(pw->pw_name, "debian-tor") == 0)
++ return 1;
++ else
++ return 0;
++}
++
++static int
++debian_config_fix_defaults()
++{
++ config_var_t *var;
++ static int fixed = 0;
++ int running_as_debian;
++
++ if (fixed) return 0;
++ fixed = 1;
++
++ running_as_debian = debian_running_as_debiantor();
++ if (running_as_debian < 0) return -1;
++ if (!running_as_debian) return 0;
++
++ var = config_find_option(&options_format, "DataDirectory");
++ tor_assert(var);
++ var->initvalue = tor_strdup("/var/lib/tor");
++
++ var = config_find_option(&options_format, "PidFile");
++ tor_assert(var);
++ var->initvalue = tor_strdup("/var/run/tor/tor.pid");
++
++ var = config_find_option(&options_format, "RunAsDaemon");
++ tor_assert(var);
++ var->initvalue = tor_strdup("1");
++
++ return 0;
++}
diff --git a/debian/patches/07_log_to_file_by_default.dpatch b/debian/patches/07_log_to_file_by_default.dpatch
index da9aaf4324..809c5ddcf3 100755
--- a/debian/patches/07_log_to_file_by_default.dpatch
+++ b/debian/patches/07_log_to_file_by_default.dpatch
@@ -23,15 +23,22 @@ esac
exit 0
@DPATCH@
-diff -urNad tor-0.1.1.17/src/or/config.c /tmp/dpep.YwfNhI/tor-0.1.1.17/src/or/config.c
---- tor-0.1.1.17/src/or/config.c
-+++ /tmp/dpep.YwfNhI/tor-0.1.1.17/src/or/config.c
-@@ -2079,7 +2079,7 @@
+diff -urNad tor~/src/or/config.c tor/src/or/config.c
+--- tor~/src/or/config.c 2006-07-24 05:15:02.576170550 +0200
++++ tor/src/or/config.c 2006-07-24 05:18:45.286651501 +0200
+@@ -2118,8 +2118,13 @@
+ REJECT("Failed to normalize old Log options. See logs for details.");
/* Special case on first boot if no Log options are given. */
- if (!options->Logs && !from_setconf)
+- if (!options->Logs && !options->RunAsDaemon && !from_setconf)
- config_line_append(&options->Logs, "Log", "notice stdout");
-+ config_line_append(&options->Logs, "Log", "notice file /var/log/tor/log");
++ if (debian_running_as_debiantor()) {
++ if (!options->Logs && !from_setconf)
++ config_line_append(&options->Logs, "Log", "notice file /var/log/tor/log");
++ } else {
++ if (!options->Logs && !options->RunAsDaemon && !from_setconf)
++ config_line_append(&options->Logs, "Log", "notice stdout");
++ }
if (options_init_logs(options, 1)<0) /* Validate the log(s) */
REJECT("Failed to validate Log options. See logs for details.");
diff --git a/debian/patches/11_tor_as_root_more_helpful.dpatch b/debian/patches/11_tor_as_root_more_helpful.dpatch
new file mode 100755
index 0000000000..96e42d5316
--- /dev/null
+++ b/debian/patches/11_tor_as_root_more_helpful.dpatch
@@ -0,0 +1,36 @@
+#! /bin/sh -e
+## 08_no_run_as_root.dpatch by <weasel@debian.org>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: No description.
+
+if [ $# -lt 1 ]; then
+ echo "`basename $0`: script expects -patch|-unpatch as argument" >&2
+ exit 1
+fi
+
+[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
+patch_opts="${patch_opts:--f --no-backup-if-mismatch} ${2:+-d $2}"
+
+case "$1" in
+ -patch) patch -p1 ${patch_opts} < $0;;
+ -unpatch) patch -R -p1 ${patch_opts} < $0;;
+ *)
+ echo "`basename $0`: script expects -patch|-unpatch as argument" >&2
+ exit 1;;
+esac
+
+exit 0
+
+@DPATCH@
+--- tor~/src/or/main.c 2006-07-23 19:31:29.000000000 +0200
++++ tor/src/or/main.c 2006-07-24 05:34:30.696138870 +0200
+@@ -1483,7 +1483,7 @@
+ #ifndef MS_WINDOWS
+ if (geteuid()==0)
+ log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, "
+- "and you probably shouldn't.");
++ "and you probably shouldn't. Maybe you are looking for the init script? '/etc/init.d/tor start'");
+ #endif
+
+ crypto_global_init(get_options()->HardwareAccel);
diff --git a/debian/rules b/debian/rules
index c593860b51..e52aae5a35 100755
--- a/debian/rules
+++ b/debian/rules
@@ -11,15 +11,20 @@ include /usr/share/dpatch/dpatch.make
# These are used for cross-compiling and for saving the configure script
# from having to guess our platform (since we know it already)
+#
+# See /usr/share/doc/autotools-dev/README.Debian.gz which suggests
+# this way of passing --build and --host. Also see the thread on
+# debian-devel './configure in debian/rules' from February/March 2006,
+# starting with <43FF212C.5020800@xs4all.nl> by Pjotr Kourzanov.
export DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
export DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-CFLAGS ?= -Wall -g
ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE))
confflags += --build $(DEB_HOST_GNU_TYPE)
else
confflags += --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE)
endif
+CFLAGS ?= -Wall -g
LOCALHOST_IP ?= $(shell getent hosts localhost | awk '{print $$1}')
diff --git a/debian/tor.init b/debian/tor.init
index e930b496ff..7a69ae621a 100644
--- a/debian/tor.init
+++ b/debian/tor.init
@@ -60,46 +60,59 @@ wait_for_deaddaemon () {
return 0
}
+
+if test ! -d $TORPIDDIR; then
+ echo "There is no $TORPIDDIR directory. Creating one for you."
+ mkdir -m 02700 "$TORPIDDIR"
+ chown debian-tor:debian-tor "$TORPIDDIR"
+fi
+
+if test ! -x $TORPIDDIR; then
+ echo "Cannot access $TORPIDDIR directory, are you root?" >&2
+ exit 1
+fi
+
+
case "$1" in
start)
if [ "$RUN_DAEMON" != "yes" ]; then
echo "Not starting $DESC (Disabled in $DEFAULTSFILE)."
- else
- if test ! -d $TORPIDDIR; then
- echo "There is no $TORPIDDIR directory. Creating one for you."
- mkdir -m 02700 "$TORPIDDIR"
- chown debian-tor:debian-tor "$TORPIDDIR"
- fi
- if test ! -x $TORPIDDIR; then echo "Cannot access $TORPIDDIR directory, are you root?" >&2; exit 1;
+ exit 0
+ fi
+
+ if [ -n "$MAX_FILEDESCRIPTORS" ]; then
+ echo -n "Raising maximum number of filedescriptors (ulimit -n) to $MAX_FILEDESCRIPTORS"
+ if ulimit -n "$MAX_FILEDESCRIPTORS" ; then
+ echo "."
else
- if [ -n "$MAX_FILEDESCRIPTORS" ]; then
- echo -n "Raising maximum number of filedescriptors (ulimit -n) to $MAX_FILEDESCRIPTORS"
- if ulimit -n "$MAX_FILEDESCRIPTORS" ; then
- echo "."
- else
- echo ": FAILED."
- fi
- fi
- echo "Starting $DESC: $NAME..."
- start-stop-daemon --start --quiet --oknodo \
- --chuid debian-tor:debian-tor \
- --pidfile $TORPID \
- $NICE \
- --exec $DAEMON -- $ARGS
- echo "done."
+ echo ": FAILED."
fi
fi
+
+ echo "Starting $DESC: $NAME..."
+ if ! su -c "$DAEMON --verify-config" debian-tor > /dev/null; then
+ echo "ABORTED: Tor configuration invalid:" >&2
+ su -c "$DAEMON --verify-config" debian-tor >&2
+ exit 1
+ fi
+
+ start-stop-daemon --start --quiet --oknodo \
+ --chuid debian-tor:debian-tor \
+ --pidfile $TORPID \
+ $NICE \
+ --exec $DAEMON -- $ARGS
+ echo "done."
;;
stop)
echo -n "Stopping $DESC: "
pid=`cat $TORPID 2>/dev/null` || true
- if test ! -d $TORPIDDIR; then echo "There is no $TORPIDDIR directory." >&2; exit 1
- elif test ! -x $TORPIDDIR; then echo "Cannot access $TORPIDDIR directory, are you root?" >&2; exit 1;
- elif test ! -f $TORPID -o -z "$pid"
- then
+
+ if test ! -f $TORPID -o -z "$pid"; then
echo "not running (there is no $TORPID)."
- elif start-stop-daemon --stop --signal INT --quiet --pidfile $TORPID --exec $DAEMON
- then
+ exit 0
+ fi
+
+ if start-stop-daemon --stop --signal INT --quiet --pidfile $TORPID --exec $DAEMON; then
wait_for_deaddaemon $pid
echo "$NAME."
elif kill -0 $pid 2>/dev/null
@@ -112,12 +125,19 @@ case "$1" in
reload|force-reload)
echo -n "Reloading $DESC configuration: "
pid=`cat $TORPID 2>/dev/null` || true
- if test ! -d $TORPIDDIR; then echo "There is no $TORPIDDIR directory." >&2; exit 1
- elif test ! -x $TORPIDDIR; then echo "Cannot access $TORPIDDIR directory, are you root?" >&2; exit 1;
- elif test ! -f $TORPID -o -z "$pid"
- then
+
+ if test ! -f $TORPID -o -z "$pid"; then
echo "not running (there is no $TORPID)."
- elif start-stop-daemon --stop --signal 1 --quiet --pidfile $TORPID --exec $DAEMON
+ exit 0
+ fi
+
+ if ! su -c "$DAEMON --verify-config" debian-tor > /dev/null; then
+ echo "ABORTED: Tor configuration invalid:" >&2
+ su -c "$DAEMON --verify-config" debian-tor >&2
+ exit 1
+ fi
+
+ if start-stop-daemon --stop --signal 1 --quiet --pidfile $TORPID --exec $DAEMON
then
echo "$NAME."
elif kill -0 $pid 2>/dev/null
@@ -128,6 +148,12 @@ case "$1" in
fi
;;
restart)
+ if ! su -c "$DAEMON --verify-config" debian-tor > /dev/null; then
+ echo "Restarting Tor ABORTED: Tor configuration invalid:" >&2
+ su -c "$DAEMON --verify-config" debian-tor >&2
+ exit 1
+ fi
+
$0 stop
sleep 1
$0 start
diff --git a/doc/HACKING b/doc/HACKING
index 23253cb341..81424a26b5 100644
--- a/doc/HACKING
+++ b/doc/HACKING
@@ -6,12 +6,14 @@
Use tor_malloc, tor_free, tor_snprintf, tor_strdup, and tor_gettimeofday
instead of their generic equivalents. (They always succeed or exit.)
- Use INLINE instead of 'inline', so that we work properly on windows.
+ You can get a full list of the compatibility functions that Tor provides
+ by looking through src/common/util.h and src/common/compat.h.
+
+ Use 'INLINE' instead of 'inline', so that we work properly on Windows.
1.2. Calling and naming conventions
- Whenever possible, functions should return -1 on error and and 0 on
- success.
+ Whenever possible, functions should return -1 on error and 0 on success.
For multi-word identifiers, use lowercase words combined with
underscores. (e.g., "multi_word_identifier"). Use ALL_CAPS for macros and
@@ -52,7 +54,7 @@
you need to (or can) do about it.
DEBUG is for everything louder than INFO.
- [XXX Proposed convention: every messages of severity INFO or higher should
+ [XXX Proposed convention: every message of severity INFO or higher should
either (A) be intelligible to end-users who don't know the Tor source; or
(B) somehow inform the end-users that they aren't expected to understand
the message (perhaps with a string like "internal error"). Option (A) is
@@ -60,8 +62,8 @@
1.5. Doxygen
- We use the 'doxygen' utility to generate documentation from our source code.
- Here's how to use it:
+ We use the 'doxygen' utility to generate documentation from our
+ source code. Here's how to use it:
1. Begin every file that should be documented with
/**
@@ -98,16 +100,17 @@
struct foo {
/** You can put the comment before an element; */
int a;
- int b; /**< Or use the less-than symbol to put the comment after the element. */
+ int b; /**< Or use the less-than symbol to put the comment
+ * after the element. */
};
5. To generate documentation from the Tor source code, type:
$ doxygen -g
- To generate a file called 'Doxyfile'. Edit that file and run 'doxygen' to
- generate the API documentation.
+ To generate a file called 'Doxyfile'. Edit that file and run
+ 'doxygen' to generate the API documentation.
- 6. See the Doxygen manual for more information; this summary just scratches
- the surface.
+ 6. See the Doxygen manual for more information; this summary just
+ scratches the surface.
diff --git a/doc/TODO b/doc/TODO
index e3fc7bc86e..bfb55c96e7 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -12,130 +12,312 @@ P - phobos claims
D Deferred
X Abandoned
-Non-Coding, Soon:
-N - Mark up spec; note unclear points about servers
-N - Mention controller libs someplace.
- D FAQ entry: why gnutls is bad/not good for tor
-P - flesh out the rest of the section 6 of the faq
-R . more pictures from ren. he wants to describe the tor handshake
-R - make a page with the hidden service diagrams.
-NR- write a spec appendix for 'being nice with tor'
- - tor-in-the-media page
- - Remove need for HACKING file.
-
-Website:
- - and remove home and make the "Tor" picture be the link to home.
- - put the logo on the website, in source form, so people can put it on
- stickers directly, etc.
-
-for 0.1.1.x-final:
-N - building on freebsd 6.0: (with multiple openssl installations)
. <nickm> "Let's try to find a way to make it run and make the version
match, but if not, let's just make it run."
- - <arma> should we detect if we have a --with-ssl-dir and try the -R
- by default, if it works?
-
- - support dir 503s better
- o clients don't log as loudly when they receive them
- - they don't count toward the 3-strikes rule
- - should there be some threshold of 503's after which we give up?
- - think about how to split "router is down" from "dirport shouldn't
- be tried for a while"?
- - authorities should *never* 503 a cache, but *should* 503 clients
- when they feel like it.
- - update dir-spec with what we decided for each of these
-
- o find N dirservers.
- o Make it no longer default for v2 dirservers to support v1.
- o non-versioning dirservers don't need to set recommended*versions.
- o non-naming dirservers don't need to have an approved-routers file.
- o What are criteria to be a dirserver? Write a policy.
- - are there other options that we haven't documented so far?
- . look at the proposed os x uninstaller:
- http://archives.seul.org/or/talk/Jan-2006/msg00038.html
-
-- Interim things:
- - provide no-cache no-index headers from the dirport?
- o remove down/useless descriptors from v1 directory?
- - exitlist should avoid outputting the same IP address twice.
-
-Deferred from 0.1.1.x:
-
- - We need a way for the authorities to declare that nodes are
- in a family. Needs design.
-
-R - streamline how we define a guard node as 'up'. document it
- somewhere.
- - Make "setconf" and "hup" behavior cleaner for LINELIST config
- options (e.g. Log). Bug 238.
-N - commit edmanm's win32 makefile to tor cvs contrib
-
-R - look into "uncounting" bytes spent on local connections. so
- we can bandwidthrate but still have fast downloads.
+ - <arma> "should we detect if we have a --with-ssl-dir and try the -R
+ by default, if it works?"
+
+Items for 0.1.2.x, real soon now:
+x - When we've been idle a long time, we stop fetching server
+ descriptors. When we then get a socks request, we build circuits
+ immediately using whatever descriptors we have, rather than waiting
+ until we've fetched correct ones.
+x - If the client's clock is too far in the past, it will drop (or
+ just not try to get) descriptors, so it'll never build circuits.
+N - when we start, remove any entryguards that are listed in excludenodes.
+N - Remember the last time we saw one of our entry guards labelled with
+ the GUARD flag. If it's been too long, it is not suitable for use.
+ If it's been really too long, remove it from the list.
+ . Figure out avoiding duplicate /24 lines
+ o automatically add /16 servers to family
+ D do it in an efficient way. keep a list of something somewhere?
+ - The right thing here is to revamp our node selection implementation.
+N - make it configurable, so people can turn it on or off.
+N - Clients stop dumping old descriptors if the network-statuses
+ claim they're still valid.
+R . If we fail to connect via an exit enclave, (warn and) try again
+ without demanding that exit node.
+ - And recognize when extending to the enclave node is failing,
+ so we can abandon then too.
+N - We need a separate list of "hidserv authorities" if we want to
+ retire moria1 from the main list.
+P - Figure out why dll's compiled in mingw don't work right in Winxp.
+P - Figure out why openssl 0.9.8c "make test" fails at sha256t test.
+
+Items for 0.1.2.x:
+ o re-enable blossom functionality: let tor servers decide if they
+ will use local search when resolving, or not.
+N - Document it.
+ - enumerate events of important things that occur in tor, so vidalia can
+ react.
+N - Backend implementation
+R - Actually list all the events (notice and warn log messages are a good
+ place to look.) Divide messages into categories, perhaps.
+N - Specify general event system
+R - Specify actual events.
+
+x - We should ship with a list of stable dir mirrors -- they're not
+ trusted like the authorities, but they'll provide more robustness
+ and diversity for bootstrapping clients.
+
+N - Simplify authority operation
+ - Follow weasel's proposal, crossed with mixminion dir config format
+ - Reject/invalidate by IP.
+
+ - Servers are easy to setup and run: being a relay is about as easy as
+ being a client.
+ - Reduce resource load
+N - Come up with good 'nicknames' automatically, or make no-nickname
+ routers workable. [Make a magic nickname "Unnamed" that can't be
+ registered and can't be looked up by nickname.]
+d - Tolerate clock skew on bridge relays.
+d - A way to examine and twiddle router flags from controller.
+ - A way to export server descriptors to controllers
+N - Event / getinfo for "when did routerdesc last change".
+d - a way to pick entries based wholly on extend_info equivalent;
+ a way to export extend_info equivalent.
+R - option to dl directory info via tor
+ - Make an option like __AllDirActionsPrivate that falls back to
+ non-Tor DL when not enough info present.
+ D Count TLS bandwidth more accurately
+
+ - Improvements to bandwidth counting
+R - look into "uncounting" bytes spent on local connections, so
+ we can bandwidthrate but still have fast downloads.
+R - "bandwidth classes", for incoming vs initiated-here conns.
+d - Write limiting; separate token bucket for write
+ - Write-limit directory responses (need to research)
+
+N - DNS improvements
+ o Option to deal with broken DNS of the "ggoogle.com? Ah, you meant
+ ads.me.com!" variety.
+ o Autodetect whether DNS is broken in this way.
+ o Don't ask reject *:* nodes for DNS unless client wants you to.
+ . Asynchronous DNS
+ o Document and rename SearchDomains, ResolvConf options
+ D Make API closer to getaddrinfo()
+ - Teach it to be able to listen for A and PTR requests to be processed.
+ Interface should be set_request_listener(sock, cb); [ cb(request) ]
+ send_reply(request, answer);
+d - Add option to use /etc/hosts?
+d - Special-case localhost?
+ - Verify that it works on windows
+ . Make reverse DNS work.
+ o Specify
+ X Implement with dnsworkers
+ (There's no point doing this, since we will throw away dnsworkers once
+ eventdns is confirmed to work everywhere.)
+ o Implement in eventdns
+ o Connect to resolve cells, server-side.
+ o Add element to routerinfo to note routers that aren't using eventdns,
+ so we can avoid sending them reverse DNS etc.
+ - Fix the bug with server-side caching, whatever is causing it.
+ . Add client-side interface
+ o SOCKS interface: specify
+ o SOCKS interface: implement
+ - Cache answers client-side
+ o Add to Tor-resolve.py
+ - Add to tor-resolve
+
+ - Performance improvements
+
+x - Better estimates in the directory of whether servers have good uptime
+ (high expected time to failure) or good guard qualities (high
+ fractional uptime).
+ - AKA Track uptime as %-of-time-up, as well as time-since-last-down
+
+ - Clients should prefer to avoid exit nodes for non-exit path positions.
+ (bug 200)
+R - spec
+x - implement
+
+ - Have a "Faster" status flag that means it. Fast2, Fast4, Fast8?
+x - spec
+d - implement
+
+ - A more efficient dir protocol.
+N - Later, servers will stop generating new descriptors simply
+ because 18 hours have passed: we must start tolerating this now.
+
+ - Critical but minor bugs, backport candidates.
+d - Failed rend desc fetches sometimes don't get retried. True/false?
+R - non-v1 authorities should not accept rend descs.
+R - support dir 503s better
+ o clients don't log as loudly when they receive them
+ - they don't count toward the 3-strikes rule
+ - should there be some threshold of 503's after which we give up?
+ - Delay when we get a lot of 503s.
+N - split "router is down" from "dirport shouldn't be tried for a while"?
+ Just a separate bit.
+ - authorities should *never* 503 a cache, but *should* 503 clients
+ when they feel like it.
+ - update dir-spec with what we decided for each of these
+
+N - provide no-cache no-index headers from the dirport?
+
+ - Windows server usability
+ - Solve the ENOBUFS problem.
+ - make tor's use of openssl operate on buffers rather than sockets,
+ so we can make use of libevent's buffer paradigm once it has one.
+ - make tor's use of libevent tolerate either the socket or the
+ buffer paradigm; includes unifying the functions in connect.c.
+ - We need a getrlimit equivalent on Windows so we can reserve some
+ file descriptors for saving files, etc. Otherwise we'll trigger
+ asserts when we're out of file descriptors and crash.
+M - rewrite how libevent does select() on win32 so it's not so very slow.
+ - Add overlapped IO
+
+ o Exitlist should avoid outputting the same IP address twice.
+
+Nd- Have a mode that doesn't write to disk much, so we can run Tor on
+ flash memory (e.g. Linksys routers or USB keys).
+ o Add AvoidDiskWrites config option.
+ - only write state file when it's "changed"
+ - stop writing identity key / fingerprint / etc every restart
+ - stop caching directory stuff -- and disable mmap?
+ - more?
+
+NR- Write path-spec.txt
+
+ - Packaging
+ - Tell people about OSX Uninstaller
+ - Quietly document NT Service options
+ - Switch canonical win32 compiler to mingw.
+
+ - Docs
+ - More prominently, we should have a recommended apps list.
+ - recommend gaim.
+ - unrecommend IE because of ftp:// bug.
+ - torrc.complete.in needs attention?
+ - we should add a preamble to tor-design saying it's out of date.
-R - Christian Grothoff's attack of infinite-length circuit.
+Topics to think about during 0.1.2.x development:
+ * Figure out incentives.
+ - (How can we make this tolerant of a bad v0?)
+ * Figure out non-clique.
+ * Figure out China.
+ - Figure out partial network knowledge.
+ - Figure out hidden services.
+ - Design next-version protocol for directories
+ - Design next-version protocol for connections
+
+For blocking-resistance scheme:
+ X allow ordinary-looking ssl for dir connections. need a new dirport
+ for this, or can we handle both ssl and non-ssl, or should we
+ entirely switch to ssl in certain cases?
+Rd- Official way to do authenticated dir conns: connect to OR port,
+ and exit to 'localhost:dir-port'.
+ - Make everybody with a dir-port implicitly accept exit to
+ localhost:dir-port.
+ - Check whether this works with one-hop circ case.
+d - need to figure out how to fetch status of a few servers from the BDA
+ without fetching all statuses. A new URL to fetch I presume?
+
+Deferred from 0.1.2.x:
+ - Directory guards
+ - RAM use in directory authorities.
+ - Memory use improvements:
+ - Look into pulling serverdescs off buffers as they arrive.
+ - Save and mmap v1 directories, and networkstatus docs; store them
+ zipped, not uncompressed.
+ - Switch cached_router_t to use mmap.
+ - What to do about reference counts on windows? (On Unix, this is
+ easy: unlink works fine. (Right?) On Windows, I have doubts. Do we
+ need to keep multiple files?)
+ - What do we do about the fact that people can't read zlib-
+ compressed files manually?
+
+ - Add IPv6 support to eventdns.c
+
+ - Refactor DNS resolve implementation
+ - Refactor exit side of resolve: do we need a connection_t?
+ - Refactor entry side of resolve: do we need a connection_t?
+
+ - A more efficient dir protocol.
+ - Authorities should fetch the network-statuses amongst each
+ other, consensus them, and advertise a communal network-status.
+ This is not so much for safety/complexity as it is to reduce
+ bandwidth requirements for Alice.
+ - How does this interact with our goal of being able to choose
+ your own dir authorities? I guess we're now assuming that all
+ dir authorities know all the other authorities in their "group"?
+ - Should we also look into a "delta since last network-status
+ checkpoint" scheme, to reduce overhead further?
+ - Extend the "r" line in network-status to give a set of buckets (say,
+ comma-separated) for that router.
+ - Buckets are deterministic based on IP address.
+ - Then clients can choose a bucket (or set of buckets) to
+ download and use.
+
+ - Improvements to versioning.
+ - When we connect to a Tor server, it sends back a cell listing
+ the IP it believes it is using. Use this to block dvorak's attack.
+ Also, this is a fine time to say what time you think it is.
+ o Verify that a new cell type is okay with deployed codebase
+ . Specify HELLO cells
+ . Figure out v0 compatibility.
+ - Implement
+
+Minor items for 0.1.2.x as time permits:
+ X If we try to publish as a nickname that's already claimed, should
+ we append a number (or increment the number) and try again? This
+ way people who read their logs can fix it as before, but people
+ who don't read their logs will still offer Tor servers.
+ - Fall back to unnamed; warn user; sent controller event.
+! - Tor should bind its ports before dropping privs, so users don't
+ have to do the ipchains dance.
+ - Rate limit exit connections to a given destination -- this helps
+ us play nice with websites when Tor users want to crawl them; it
+ also introduces DoS opportunities.
+! - The bw_accounting file should get merged into the state file.
+R - Streamline how we pick entry nodes.
+! - Better installers and build processes.
+ - Commit edmanm's win32 makefile to tor contrib, or write a new one.
+ - Christian Grothoff's attack of infinite-length circuit.
the solution is to have a separate 'extend-data' cell type
which is used for the first N data cells, and only
extend-data cells can be extend requests.
- - Specify, including thought about
- - Implement
-
-R - When we connect to a Tor server, it sends back a cell listing
- the IP it believes it is using. Use this to block dvorak's attack.
- Also, this is a fine time to say what time you think it is.
- - Verify that a new cell type is okay with deployed codebase
- - Specify
- - Implement
-
-R - failed rend desc fetches sometimes don't get retried.
-
-N - Display the reasons in 'destroy' and 'truncated' cells under some
+ - Specify, including thought about anonymity implications.
+ - Display the reasons in 'destroy' and 'truncated' cells under some
circumstances?
-
+ - We need a way for the authorities to declare that nodes are
+ in a family. Also, it kinda sucks that family declarations use O(N^2)
+ space in the descriptors.
- If the server is spewing complaints about raising your ulimit -n,
we should add a note about this to the server descriptor so other
people can notice too.
+ - cpu fixes:
+ - see if we should make use of truncate to retry
+ X kill dns workers more slowly
+ . Directory changes
+ . Some back-out mechanism for auto-approval
+ - a way of rolling back approvals to before a timestamp
+ - Consider minion-like fingerprint file/log combination.
+ - packaging and ui stuff:
+ . multiple sample torrc files
+ . figure out how to make nt service stuff work?
+ . Document it.
+ - Vet all pending installer patches
+ - Win32 installer plus privoxy, sockscap/freecap, etc.
+ - Vet win32 systray helper code
- - We need a getrlimit equivalent on Windows so we can reserve some
- file descriptors for saving files, etc. Otherwise we'll trigger
- asserts when we're out of file descriptors and crash.
-
- X <weasel> it would be nice to support a unix socket for the control thing.
- The main motivation behind this was that we could let unix permissions
- take care of the authentication step: everybody who can connect to the
- socket is authenticated. However, the linux unix(7) manual page suggests
- that requiring read/write permissions on the socket in order to use it
- is Linux specific, and that many BSD-derived systems ignore the permissions
- on the socket file. Portable programs should not rely on this feature for
- security, therefore the motivation for this feature is gone.
-
- - the tor client can do the "automatic proxy config url" thing?
-
-R - clients prefer to avoid exit nodes for non-exit path positions.
-
- - Automatically determine what ports are reachable and start using
- those, if circuits aren't working and it's a pattern we recognize
- ("port 443 worked once and port 9001 keeps not working").
-
-N - Should router info have a pointer to routerstatus?
- - We should at least do something about the duplicated fields.
-
-N . Additional controller features
+ - Improve controller
+ - a NEWSTATUS event similar to NEWDESC.
- change circuit status events to give more details, like purpose,
whether they're internal, when they become dirty, when they become
too dirty for further circuits, etc.
-R - What do we want here, exactly?
-N - Specify and implement it.
+ - What do we want here, exactly?
+ - Specify and implement it.
- Change stream status events analogously.
-R - What do we want here, exactly?
-N - Specify and implement it.
+ - What do we want here, exactly?
+ - Specify and implement it.
- Make other events "better".
- Change stream status events analogously.
-R - What do we want here, exactly?
-N - Specify and implement it.
+ - What do we want here, exactly?
+ - Specify and implement it.
- Make other events "better" analogously
-R - What do we want here, exactly?
-N - Specify and implement it.
+ - What do we want here, exactly?
+ - Specify and implement it.
. Expose more information via getinfo:
- import and export rendezvous descriptors
- Review all static fields for additional candidates
@@ -145,57 +327,19 @@ N - Specify and implement it.
- It would be nice to request address lookups from the controller
without using SOCKS.
- Make everything work with hidden services
-
- X switch accountingmax to count total in+out, not either in or
- out. it's easy to move in this direction (not risky), but hard to
- back out if we decide we prefer it the way it already is. hm.
-
- - cpu fixes:
- - see if we should make use of truncate to retry
-R - kill dns workers more slowly
-
- . Directory changes
- . Some back-out mechanism for auto-approval
- - a way of rolling back approvals to before a timestamp
- - Consider minion-like fingerprint file/log combination.
-
+ - Directory system improvements
- config option to publish what ports you listen on, beyond
ORPort/DirPort. It should support ranges and bit prefixes (?) too.
- Parse this.
- Relay this in networkstatus.
+ o smartlist_uniq(): We have at least 3 places that check a smartlist for
+ duplicates and then removes them: networkstatus_parse_from_string(),
+ sort_version_list(), and router_rebuild_descriptor(). This should probably
+ get its own function that takes a comparator and a delete function.
- - Non-directories don't need to keep descriptors in memory.
- o Make descriptor-fetching happen via an indirection function.
- - Remember file and offset.
- - Keep a journal FD for appending router descriptors.
-
- - packaging and ui stuff:
- . multiple sample torrc files
- - uninstallers
- . for os x
- . figure out how to make nt service stuff work?
- . Document it.
- o Add version number to directory.
-N - Vet all pending installer patches
- - Win32 installer plus privoxy, sockscap/freecap, etc.
- - Vet win32 systray helper code
-
- - document:
- o torcp needs more attention in the tor-doc-win32.
- - recommend gaim.
- - unrecommend IE because of ftp:// bug.
- - torrc.complete.in needs attention?
-
- - Bind to random port when making outgoing connections to Tor servers,
- to reduce remote sniping attacks.
- - Have new people be in limbo and need to demonstrate usefulness
- before we approve them.
- - Clients should estimate their skew as median of skew from servers
- over last N seconds.
- - Security
- - Alices avoid duplicate class C nodes.
- - Analyze how bad the partitioning is or isn't.
-
+Future version:
+ - Configuration format really wants sections.
+ - Good RBL substitute.
. Update the hidden service stuff for the new dir approach.
- switch to an ascii format, maybe sexpr?
- authdirservers publish blobs of them.
@@ -207,45 +351,20 @@ N - Vet all pending installer patches
- come up with a few more auth mechanisms.
- auth mechanisms to let hidden service midpoint and responder filter
connection requests.
-
- . Come up with a coherent strategy for bandwidth buckets and TLS. (The
- logic for reading from TLS sockets is likely to overrun the bandwidth
- buckets under heavy load. (Really, the logic was never right in the
- first place.) Also, we should audit all users of get_pending_bytes().)
- - Make it harder to circumvent bandwidth caps: look at number of bytes
- sent across sockets, not number sent inside TLS stream.
-
+ - Bind to random port when making outgoing connections to Tor servers,
+ to reduce remote sniping attacks.
+ - Have new people be in limbo and need to demonstrate usefulness
+ before we approve them.
+ - Clients should estimate their skew as median of skew from servers
+ over last N seconds.
- Make router_is_general_exit() a bit smarter once we're sure what it's for.
-
- - rewrite how libevent does select() on win32 so it's not so very slow.
- - Write limiting; separate token bucket for write
- Audit everything to make sure rend and intro points are just as likely to
be us as not.
- Do something to prevent spurious EXTEND cells from making middleman
nodes connect all over. Rate-limit failed connections, perhaps?
-
-
-Major items for 0.1.2.x:
-
- - Directory guards
-R - Server usability
-N - Better hidden service performance
- - Improve controller
- - Asynchronous DNS
- - Better estimates in the directory of whether servers have good uptime
- (high expected time to failure) or good guard qualities (high
- fractional uptime).
- - memory usage on dir servers.
- copy less!
-N - oprofile including kernel time.
-
-Topics to think about during 0.1.2.x development:
- - Figure out non-clique.
- - Figure out partial network knowledge.
- - Figure out incentives.
-
-
-Future version:
+ - Automatically determine what ports are reachable and start using
+ those, if circuits aren't working and it's a pattern we recognize
+ ("port 443 worked once and port 9001 keeps not working").
- Limit to 2 dir, 2 OR, N SOCKS connections per IP.
- Handle full buffers without totally borking
- Rate-limit OR and directory connections overall and per-IP and
@@ -255,13 +374,11 @@ Future version:
- DoS protection: TLS puzzles, public key ops, bandwidth exhaustion.
- Specify?
- tor-resolve script should use socks5 to get better error messages.
- - Track uptime as %-of-time-up, as well as time-since-last-down.
- hidserv offerers shouldn't need to define a SocksPort
* figure out what breaks for this, and do it.
- - Relax clique assumptions.
- X start handling server descriptors without a socksport?
- tor should be able to have a pool of outgoing IP addresses
that it is able to rotate through. (maybe)
+ - Specify; implement.
- let each hidden service (or other thing) specify its own
OutboundBindAddress?
@@ -284,3 +401,20 @@ Blue-sky:
we've seen in the wild.
(Pending a user who needs this)
+Non-Coding:
+ - Mark up spec; note unclear points about servers
+ - Mention controller libs someplace.
+ . more pictures from ren. he wants to describe the tor handshake
+NR- write a spec appendix for 'being nice with tor'
+ - tor-in-the-media page
+ - Remove need for HACKING file.
+ - Figure out licenses for website material.
+
+Website:
+ - and remove home and make the "Tor" picture be the link to home.
+ - put the logo on the website, in source form, so people can put it on
+ stickers directly, etc.
+R - make a page with the hidden service diagrams.
+
+ - ask Jan to be the translation coordinator? add to volunteer page.
+
diff --git a/doc/control-spec-v0.txt b/doc/control-spec-v0.txt
index df2c054010..4c64040acb 100644
--- a/doc/control-spec-v0.txt
+++ b/doc/control-spec-v0.txt
@@ -4,8 +4,9 @@ $Id$
-1. Deprecation
-THIS PROTOCOL IS DEPRECATED. It is still documented here because it is the
-only Tor control protocol supported in the Tor implementation right now.
+THIS PROTOCOL IS DEPRECATED. It is still documented here because Tor
+0.1.1.x happens to support much of it; but the support for v0 is not
+maintained, so you should expect it to rot in unpredictable ways.
0. Scope
diff --git a/doc/control-spec.txt b/doc/control-spec.txt
index 8cfc36ceba..a79973e3e0 100644
--- a/doc/control-spec.txt
+++ b/doc/control-spec.txt
@@ -188,6 +188,8 @@ $Id$
or none.
NOTE: "EXTENDED" is only supported in Tor 0.1.1.9-alpha or later.
+ XXX Need to describe what each event does and is for -RD
+
3.5. AUTHENTICATE
Sent from the client to the server. The syntax is:
@@ -316,6 +318,13 @@ $Id$
"config-file" -- The location of Tor's configuration file ("torrc").
+ ["exit-policy/prepend" -- The default exit policy lines that Tor will
+ *prepend* to the ExitPolicy config option.
+ -- Never implemented. Useful?]
+
+ "exit-policy/default" -- The default exit policy lines that Tor will
+ *append* to the ExitPolicy config option.
+
"desc/id/<OR identity>" or "desc/name/<OR nickname>" -- the latest
server descriptor for a given OR, NUL-terminated.
@@ -337,10 +346,13 @@ $Id$
via the control interface; the 'all' target returns the mappings
set through any mechanism.
+ "address" -- the best guess at our external IP address. If we
+ have no guess, return a 551 error.
+
"circuit-status"
A series of lines as for a circuit status event. Each line is of
the form:
- CircuitID SP CircStatus SP Path CRLF
+ CircuitID SP CircStatus [SP Path] CRLF
"stream-status"
A series of lines as for a stream status event. Each is of the form:
@@ -675,7 +687,7 @@ $Id$
The syntax is:
- "650" SP "CIRC" SP CircuitID SP CircStatus SP Path
+ "650" SP "CIRC" SP CircuitID SP CircStatus [SP Path]
CircStatus =
"LAUNCHED" / ; circuit ID assigned to new circuit
@@ -686,6 +698,9 @@ $Id$
Path = ServerID *("," ServerID)
+ The path is provided only when the circuit has been extended at least one
+ hop.
+
4.1.2. Stream status changed
The syntax is:
@@ -768,6 +783,11 @@ $Id$
file named "control_auth_cookie" into its data directory. To authenticate,
the controller must send the contents of this file.
+ [With the v1 controller protocol, what we really mean is that you should
+ send the base16 of the contents of this file. Is this it, or is there
+ more to it? Should we write a control_auth_cookie.asc file too that
+ makes this step easier for people doing it manually? -RD]
+
If the 'HashedControlPassword' option is set, it must contain the salted
hash of a secret password. The salted hash is computed according to the
S2K algorithm in RFC 2440 (OpenPGP), and prefixed with the s2k specifier.
diff --git a/doc/design-paper/blocking.tex b/doc/design-paper/blocking.tex
new file mode 100644
index 0000000000..ebc677ab82
--- /dev/null
+++ b/doc/design-paper/blocking.tex
@@ -0,0 +1,352 @@
+\documentclass{llncs}
+
+\usepackage{url}
+\usepackage{amsmath}
+\usepackage{epsfig}
+
+%\setlength{\textwidth}{5.9in}
+%\setlength{\textheight}{8.4in}
+%\setlength{\topmargin}{.5cm}
+%\setlength{\oddsidemargin}{1cm}
+%\setlength{\evensidemargin}{1cm}
+
+\newenvironment{tightlist}{\begin{list}{$\bullet$}{
+ \setlength{\itemsep}{0mm}
+ \setlength{\parsep}{0mm}
+ % \setlength{\labelsep}{0mm}
+ % \setlength{\labelwidth}{0mm}
+ % \setlength{\topsep}{0mm}
+ }}{\end{list}}
+
+\begin{document}
+
+\title{Design of a blocking-resistant anonymity system}
+
+\author{}
+
+\maketitle
+\pagestyle{plain}
+
+\begin{abstract}
+
+Websites around the world are increasingly being blocked by
+government-level firewalls. Many people use anonymizing networks like
+Tor to contact sites without letting an attacker trace their activities,
+and as an added benefit they are no longer affected by local censorship.
+But if the attacker simply denies access to the Tor network itself,
+blocked users can no longer benefit from the security Tor offers.
+
+Here we describe a design that uses the current Tor network as a
+building block to provide an anonymizing network that resists blocking
+by government-level attackers.
+
+\end{abstract}
+
+\section{Introduction and Goals}
+
+Websites like Wikipedia and Blogspot are increasingly being blocked by
+government-level firewalls around the world.
+
+China is the third largest user base for Tor clients~\cite{geoip-tor}.
+Many people already want it, and the current Tor design is easy to block
+(by blocking the directory authorities, by blocking all the server
+IP addresses, or by filtering the signature of the Tor TLS handshake).
+
+Now that we've got an overlay network, we're most of the way there in
+terms of building a blocking-resistant tool.
+
+And it improves the anonymity that Tor can provide to add more different
+classes of users and goals to the Tor network.
+
+\subsection{A single system that works for multiple blocked domains}
+
+We want this to work for people in China, people in Iran, people in
+Thailand, people in firewalled corporate networks, etc. The blocking
+censor will be at different stages of the arms race in different places;
+and likely the list of blocked addresses will be different in each
+location too.
+
+
+\section{Adversary assumptions}
+\label{sec:adversary}
+
+Three main network attacks by censors currently:
+
+\begin{tightlist}
+\item Block destination by string matches in TCP packets.
+
+\item Block destination by IP address.
+
+\item Intercept DNS requests.
+\end{tightlist}
+
+Assume the network firewall has very limited CPU~\cite{clayton06}.
+
+Assume that readers of blocked content will not be punished much
+(relative to writers).
+
+Assume that while various different adversaries can coordinate and share
+notes, there will be a significant time lag between one attacker learning
+how to overcome a facet of our design and other attackers picking it up.
+
+
+
+
+\section{Related schemes}
+
+\subsection{public single-hop proxies}
+
+\subsection{personal single-hop proxies}
+
+Easier to deploy; might not require client-side software.
+
+\subsection{break your sensitive strings into multiple tcp packets}
+
+\subsection{steganography}
+
+% \subsection{}
+
+\section{Useful building blocks}
+
+\subsection{Tor}
+
+Anonymizing networks such as
+Tor~\cite{tor-design}
+aim to hide not only what is being said, but also who is
+communicating with whom, which users are using which websites, and so on.
+These systems have a broad range of users, including ordinary citizens
+who want to avoid being profiled for targeted advertisements, corporations
+who don't want to reveal information to their competitors, and law
+enforcement and government intelligence agencies who need
+to do operations on the Internet without being noticed.
+
+Tor provides three security properties:
+\begin{tightlist}
+\item A local observer can't learn, or influence, your destination.
+\item The destination, or somebody watching the destination, can't learn
+your location.
+\item No single piece of the infrastructure can link you to your
+destination.
+\end{tightlist}
+
+We care most clearly about property number 1. But when the arms race
+progresses, property 2 will become important -- so the blocking adversary
+can't learn user+destination just by volunteering a relay. It's not so
+clear to see that property 3 is important, but consider websites and
+services that are pressured into treating clients from certain network
+locations differently.
+
+Other benefits:
+
+\begin{tightlist}
+\item Separates the role of relay from the role of exit node.
+
+\item (Re)builds circuits automatically in the background, based on
+whichever paths work.
+\end{tightlist}
+
+\subsection{Tor circuits}
+
+can build arbitrary overlay paths given a set of descriptors~\cite{blossom}
+
+\subsection{Tor directory servers}
+
+\subsection{Tor user base}
+
+\section{The Design, version one}
+
+\subsection{Bridge relays}
+
+Some Tor users on the free side of the network will opt to become
+bridge relays. They will relay a bit of traffic and won't need to allow
+exits. They sign up on the bridge directory authorities, below.
+
+...need to outline instructions for a Tor config that will publish
+to an alternate directory authority, and for controller commands
+that will do this cleanly.
+
+\subsection{The bridge directory authority (BDA)}
+
+They aggregate server descriptors just like the main authorities, and
+answer all queries as usual, except they don't publish network statuses.
+
+So once you know a bridge relay's key, you can get the most recent
+server descriptor for it.
+
+XXX need to figure out how to fetch some server statuses from the BDA
+without fetching all statuses. A new URL to fetch I presume?
+
+\subsection{Blocked users}
+
+If a blocked user has a server descriptor for one working bridge relay,
+then he can make secure connections to the BDA to update his knowledge
+about other bridge
+relays, and he can make secure connections to the main Tor network
+and directory servers to build circuits and connect to the rest of
+the Internet.
+
+So now we've reduced the problem from how to circumvent the firewall
+for all transactions (and how to know that the pages you get have not
+been modified by the local attacker) to how to learn about a working
+bridge relay.
+
+The simplest format for communicating information about a bridge relay
+is as an IP address and port for its directory cache. From there, the
+user can ask the directory cache for an up-to-date copy of that bridge
+relay's server descriptor, including its current circuit keys, the port
+it uses for Tor connections, and so on.
+
+However, connecting directly to the directory cache involves a plaintext
+http request, so the censor could create a firewall signature for the
+request and/or its response, thus preventing these connections. If that
+happens, the first fix is to use SSL -- not for authentication, but
+just for encryption so requests look different every time.
+
+There's another possible attack here: since we only learn an IP address
+and port, a local attacker could intercept our directory request and
+give us some other server descriptor. But notice that we don't need
+strong authentication for the bridge relay. Since the Tor client will
+ship with trusted keys for the bridge directory authority and the Tor
+network directory authorities, the user can decide if the bridge relays
+are lying to him or not.
+
+Once the Tor client has fetched the server descriptor at least once,
+it should remember the identity key fingerprint for that bridge relay.
+If the bridge relay moves to a new IP address, the client can then
+use the bridge directory authority to look up a fresh server descriptor
+using this fingerprint.
+
+another option is to conclude
+that it will be better to tunnel through a Tor circuit when fetching them.
+
+The following section describes ways to bootstrap knowledge of your first
+bridge relay, and ways to maintain connectivity once you know a few
+bridge relays.
+
+\section{Discovering and maintaining working bridge relays}
+
+\subsection{Initial network discovery}
+
+We make the assumption that the firewall is not perfect. People can
+get around it through the usual means, or they know a friend who can.
+If they can't get around it at all, then we can't help them -- they
+should go meet more people.
+
+Thus they can reach the BDA. From here we either assume a social
+network or other mechanism for learning IP:dirport or key fingerprints
+as above, or we assume an account server that allows us to limit the
+number of new bridge relays an external attacker can discover.
+
+
+
+\section{The Design, version two}
+
+\item A blinded token, which can be exchanged at the BDA (assuming you
+can reach it) for a new IP:dirport or server descriptor.
+
+\subsection{The account server}
+
+Users can establish reputations, perhaps based on social network
+connectivity, perhaps based on not getting their bridge relays blocked,
+
+
+
+\section{Other issues}
+
+\subsection{How many bridge relays should you know about?}
+
+If they're ordinary Tor users on cable modem or DSL, many of them will
+disappear periodically. How many bridge relays should a blockee know
+about before he's likely to have at least one up at any given point?
+
+The related question is: if the bridge relays change IP addresses
+periodically, how often does the blockee need to "check in" in order
+to keep from being cut out of the loop?
+
+\subsection{How do we know if a bridge relay has been blocked?}
+
+We need some mechanism for testing reachability from inside the
+blocked area. The easiest answer is for certain users inside
+the area to sign up as testing relays, and then we can route through
+them and see if it works.
+
+First problem is that different network areas block different net masks,
+and it will likely be hard to know which users are in which areas. So
+if a bridge relay isn't reachable, is that because of a network block
+somewhere, because of a problem at the bridge relay, or just a temporary
+outage?
+
+Second problem is that if we pick random users to test random relays, the
+adversary should sign up users on the inside, and enumerate the relays
+we test. But it seems dangerous to just let people come forward and
+declare that things are blocked for them, since they could be tricking
+us. (This matters even moreso if our reputation system above relies on
+whether things get blocked to punish or reward.)
+
+
+
+
+\subsection{Tunneling directory lookups through Tor}
+
+All you need to do is bootstrap, and then you can use
+your Tor connection to maintain your Tor connection,
+including doing secure directory fetches.
+
+\subsection{Predictable SSL ports}
+
+We should encourage most servers to listen on port 443, which is
+where SSL normally listens.
+
+Is that all it will take, or should we set things up so some fraction
+of them pick random ports? I can see that both helping and hurting.
+
+\subsection{Predictable TLS handshakes}
+
+Right now Tor has some predictable strings in its TLS handshakes.
+These can be removed; but should they be replaced with nothing, or
+should we try to emulate some popular browser? In any case our
+protocol demands a pair of certs on both sides -- how much will this
+make Tor handshakes stand out?
+
+\section{Anonymity issues from becoming a bridge relay}
+
+You can actually harm your anonymity by relaying traffic in Tor. This is
+the same issue that ordinary Tor servers face. On the other hand, it
+provides improved anonymity against some attacks too:
+
+\begin{verbatim}
+http://wiki.noreply.org/noreply/TheOnionRouter/TorFAQ#ServerAnonymity
+\end{verbatim}
+
+\subsection{Cablemodem users don't provide important websites}
+
+...so our adversary could just block all DSL and cablemodem networks,
+and for the most part only our bridge relays would be affected.
+
+The first answer is to aim to get volunteers both from traditionally
+``consumer'' networks and also from traditionally ``producer'' networks.
+
+The second answer (not so good) would be to encourage more use of consumer
+networks for popular and useful websites.
+
+\section{Future designs}
+
+\subsection{Bridges inside the blocked network too}
+
+Assuming actually crossing the firewall is the risky part of the
+operation, can we have some bridge relays inside the blocked area too,
+and more established users can use them as relays so they don't need to
+communicate over the firewall directly at all? A simple example here is
+to make new blocked users into internal bridges also -- so they sign up
+on the BDA as part of doing their query, and we give out their addresses
+rather than (or along with) the external bridge addresses. This design
+is a lot trickier because it brings in the complexity of whether the
+internal bridges will remain available, can maintain reachability with
+the outside world, etc.
+
+Hidden services as bridges.
+
+%\bibliographystyle{plain} \bibliography{tor-design}
+
+\end{document}
+
diff --git a/doc/dir-spec-v1.txt b/doc/dir-spec-v1.txt
new file mode 100644
index 0000000000..d5381c0cbe
--- /dev/null
+++ b/doc/dir-spec-v1.txt
@@ -0,0 +1,315 @@
+$Id$
+
+ Tor Protocol Specification
+
+ Roger Dingledine
+ Nick Mathewson
+
+0. Prelimaries
+
+ THIS SPECIFICATION IS OBSOLETE.
+
+ This document specifies the Tor directory protocol as used in version
+ 0.1.0.x and earlier. See dir-spec.txt for a current version.
+
+1. Basic operation
+
+ There is a small number of directory authorities, and a larger number of
+ caches. Client and servers know public keys for the directory authorities.
+ Tor servers periodically upload self-signed "router descriptors" to the
+ directory authorities. Each authority publishes a self-signed "directory"
+ (containing all the router descriptors it knows, and a statement on which
+ are running) and a self-signed "running routers" document containing only
+ the statement on which routers are running.
+
+ All Tors periodically download these documents, downloading the directory
+ less frequently than they do the "running routers" document. Clients
+ preferentially download from caches rather than authorities.
+
+1.1. Document format
+
+ Router descriptors, directories, and running-routers documents all obey the
+ following lightweight extensible information format.
+
+ The highest level object is a Document, which consists of one or more
+ Items. Every Item begins with a KeywordLine, followed by one or more
+ Objects. A KeywordLine begins with a Keyword, optionally followed by
+ whitespace and more non-newline characters, and ends with a newline. A
+ Keyword is a sequence of one or more characters in the set [A-Za-z0-9-].
+ An Object is a block of encoded data in pseudo-Open-PGP-style
+ armor. (cf. RFC 2440)
+
+ More formally:
+
+ Document ::= (Item | NL)+
+ Item ::= KeywordLine Object*
+ KeywordLine ::= Keyword NL | Keyword WS ArgumentsChar+ NL
+ Keyword = KeywordChar+
+ KeywordChar ::= 'A' ... 'Z' | 'a' ... 'z' | '0' ... '9' | '-'
+ ArgumentChar ::= any printing ASCII character except NL.
+ WS = (SP | TAB)+
+ Object ::= BeginLine Base-64-encoded-data EndLine
+ BeginLine ::= "-----BEGIN " Keyword "-----" NL
+ EndLine ::= "-----END " Keyword "-----" NL
+
+ The BeginLine and EndLine of an Object must use the same keyword.
+
+ When interpreting a Document, software MUST reject any document containing a
+ KeywordLine that starts with a keyword it doesn't recognize.
+
+ The "opt" keyword is reserved for non-critical future extensions. All
+ implementations MUST ignore any item of the form "opt keyword ....." when
+ they would not recognize "keyword ....."; and MUST treat "opt keyword ....."
+ as synonymous with "keyword ......" when keyword is recognized.
+
+8.2. Router descriptor format.
+
+ Every router descriptor MUST start with a "router" Item; MUST end with a
+ "router-signature" Item and an extra NL; and MUST contain exactly one
+ instance of each of the following Items: "published" "onion-key" "link-key"
+ "signing-key" "bandwidth". Additionally, a router descriptor MAY contain
+ any number of "accept", "reject", "fingerprint", "uptime", and "opt" Items.
+ Other than "router" and "router-signature", the items may appear in any
+ order.
+
+ The items' formats are as follows:
+ "router" nickname address ORPort SocksPort DirPort
+
+ Indicates the beginning of a router descriptor. "address"
+ must be an IPv4 address in dotted-quad format. The last
+ three numbers indicate the TCP ports at which this OR exposes
+ functionality. ORPort is a port at which this OR accepts TLS
+ connections for the main OR protocol; SocksPort is deprecated and
+ should always be 0; and DirPort is the port at which this OR accepts
+ directory-related HTTP connections. If any port is not supported,
+ the value 0 is given instead of a port number.
+
+ "bandwidth" bandwidth-avg bandwidth-burst bandwidth-observed
+
+ Estimated bandwidth for this router, in bytes per second. The
+ "average" bandwidth is the volume per second that the OR is willing
+ to sustain over long periods; the "burst" bandwidth is the volume
+ that the OR is willing to sustain in very short intervals. The
+ "observed" value is an estimate of the capacity this server can
+ handle. The server remembers the max bandwidth sustained output
+ over any ten second period in the past day, and another sustained
+ input. The "observed" value is the lesser of these two numbers.
+
+ "platform" string
+
+ A human-readable string describing the system on which this OR is
+ running. This MAY include the operating system, and SHOULD include
+ the name and version of the software implementing the Tor protocol.
+
+ "published" YYYY-MM-DD HH:MM:SS
+
+ The time, in GMT, when this descriptor was generated.
+
+ "fingerprint"
+
+ A fingerprint (a HASH_LEN-byte of asn1 encoded public key, encoded
+ in hex, with a single space after every 4 characters) for this router's
+ identity key. A descriptor is considered invalid (and MUST be
+ rejected) if the fingerprint line does not match the public key.
+
+ [We didn't start parsing this line until Tor 0.1.0.6-rc; it should
+ be marked with "opt" until earlier versions of Tor are obsolete.]
+
+ "hibernating" 0|1
+
+ If the value is 1, then the Tor server was hibernating when the
+ descriptor was published, and shouldn't be used to build circuits.
+
+ [We didn't start parsing this line until Tor 0.1.0.6-rc; it should
+ be marked with "opt" until earlier versions of Tor are obsolete.]
+
+ "uptime"
+
+ The number of seconds that this OR process has been running.
+
+ "onion-key" NL a public key in PEM format
+
+ This key is used to encrypt EXTEND cells for this OR. The key MUST
+ be accepted for at least XXXX hours after any new key is published in
+ a subsequent descriptor.
+
+ "signing-key" NL a public key in PEM format
+
+ The OR's long-term identity key.
+
+ "accept" exitpattern
+ "reject" exitpattern
+
+ These lines, in order, describe the rules that an OR follows when
+ deciding whether to allow a new stream to a given address. The
+ 'exitpattern' syntax is described below.
+
+ "router-signature" NL Signature NL
+
+ The "SIGNATURE" object contains a signature of the PKCS1-padded
+ hash of the entire router descriptor, taken from the beginning of the
+ "router" line, through the newline after the "router-signature" line.
+ The router descriptor is invalid unless the signature is performed
+ with the router's identity key.
+
+ "contact" info NL
+
+ Describes a way to contact the server's administrator, preferably
+ including an email address and a PGP key fingerprint.
+
+ "family" names NL
+
+ 'Names' is a whitespace-separated list of server nicknames. If two ORs
+ list one another in their "family" entries, then OPs should treat them
+ as a single OR for the purpose of path selection.
+
+ For example, if node A's descriptor contains "family B", and node B's
+ descriptor contains "family A", then node A and node B should never
+ be used on the same circuit.
+
+ "read-history" YYYY-MM-DD HH:MM:SS (NSEC s) NUM,NUM,NUM,NUM,NUM... NL
+ "write-history" YYYY-MM-DD HH:MM:SS (NSEC s) NUM,NUM,NUM,NUM,NUM... NL
+
+ Declare how much bandwidth the OR has used recently. Usage is divided
+ into intervals of NSEC seconds. The YYYY-MM-DD HH:MM:SS field defines
+ the end of the most recent interval. The numbers are the number of
+ bytes used in the most recent intervals, ordered from oldest to newest.
+
+ [We didn't start parsing these lines until Tor 0.1.0.6-rc; they should
+ be marked with "opt" until earlier versions of Tor are obsolete.]
+
+2.1. Nonterminals in routerdescriptors
+
+ nickname ::= between 1 and 19 alphanumeric characters, case-insensitive.
+
+ exitpattern ::= addrspec ":" portspec
+ portspec ::= "*" | port | port "-" port
+ port ::= an integer between 1 and 65535, inclusive.
+ addrspec ::= "*" | ip4spec | ip6spec
+ ipv4spec ::= ip4 | ip4 "/" num_ip4_bits | ip4 "/" ip4mask
+ ip4 ::= an IPv4 address in dotted-quad format
+ ip4mask ::= an IPv4 mask in dotted-quad format
+ num_ip4_bits ::= an integer between 0 and 32
+ ip6spec ::= ip6 | ip6 "/" num_ip6_bits
+ ip6 ::= an IPv6 address, surrounded by square brackets.
+ num_ip6_bits ::= an integer between 0 and 128
+
+ Ports are required; if they are not included in the router
+ line, they must appear in the "ports" lines.
+
+3. Directory format
+
+ A Directory begins with a "signed-directory" item, followed by one each of
+ the following, in any order: "recommended-software", "published",
+ "router-status", "dir-signing-key". It may include any number of "opt"
+ items. After these items, a directory includes any number of router
+ descriptors, and a single "directory-signature" item.
+
+ "signed-directory"
+
+ Indicates the start of a directory.
+
+ "published" YYYY-MM-DD HH:MM:SS
+
+ The time at which this directory was generated and signed, in GMT.
+
+ "dir-signing-key"
+
+ The key used to sign this directory; see "signing-key" for format.
+
+ "recommended-software" comma-separated-version-list
+
+ A list of which versions of which implementations are currently
+ believed to be secure and compatible with the network.
+
+ "running-routers" whitespace-separated-list
+
+ A description of which routers are currently believed to be up or
+ down. Every entry consists of an optional "!", followed by either an
+ OR's nickname, or "$" followed by a hexadecimal encoding of the hash
+ of an OR's identity key. If the "!" is included, the router is
+ believed not to be running; otherwise, it is believed to be running.
+ If a router's nickname is given, exactly one router of that nickname
+ will appear in the directory, and that router is "approved" by the
+ directory server. If a hashed identity key is given, that OR is not
+ "approved". [XXXX The 'running-routers' line is only provided for
+ backward compatibility. New code should parse 'router-status'
+ instead.]
+
+ "router-status" whitespace-separated-list
+
+ A description of which routers are currently believed to be up or
+ down, and which are verified or unverified. Contains one entry for
+ every router that the directory server knows. Each entry is of the
+ format:
+
+ !name=$digest [Verified router, currently not live.]
+ name=$digest [Verified router, currently live.]
+ !$digest [Unverified router, currently not live.]
+ or $digest [Unverified router, currently live.]
+
+ (where 'name' is the router's nickname and 'digest' is a hexadecimal
+ encoding of the hash of the routers' identity key).
+
+ When parsing this line, clients should only mark a router as
+ 'verified' if its nickname AND digest match the one provided.
+
+ "directory-signature" nickname-of-dirserver NL Signature
+
+ The signature is computed by computing the digest of the
+ directory, from the characters "signed-directory", through the newline
+ after "directory-signature". This digest is then padded with PKCS.1,
+ and signed with the directory server's signing key.
+
+ If software encounters an unrecognized keyword in a single router descriptor,
+ it MUST reject only that router descriptor, and continue using the
+ others. Because this mechanism is used to add 'critical' extensions to
+ future versions of the router descriptor format, implementation should treat
+ it as a normal occurrence and not, for example, report it to the user as an
+ error. [Versions of Tor prior to 0.1.1 did this.]
+
+ If software encounters an unrecognized keyword in the directory header,
+ it SHOULD reject the entire directory.
+
+4. Network-status descriptor
+
+ A "network-status" (a.k.a "running-routers") document is a truncated
+ directory that contains only the current status of a list of nodes, not
+ their actual descriptors. It contains exactly one of each of the following
+ entries.
+
+ "network-status"
+
+ Must appear first.
+
+ "published" YYYY-MM-DD HH:MM:SS
+
+ (see 8.3 above)
+
+ "router-status" list
+
+ (see 8.3 above)
+
+ "directory-signature" NL signature
+
+ (see 8.3 above)
+
+5. Behavior of a directory server
+
+ lists nodes that are connected currently
+ speaks HTTP on a socket, spits out directory on request
+
+ Directory servers listen on a certain port (the DirPort), and speak a
+ limited version of HTTP 1.0. Clients send either GET or POST commands.
+ The basic interactions are:
+ "%s %s HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s\r\n\r\n",
+ command, url, content-length, host.
+ Get "/tor/" to fetch a full directory.
+ Get "/tor/dir.z" to fetch a compressed full directory.
+ Get "/tor/running-routers" to fetch a network-status descriptor.
+ Post "/tor/" to post a server descriptor, with the body of the
+ request containing the descriptor.
+
+ "host" is used to specify the address:port of the dirserver, so
+ the request can survive going through HTTP proxies.
+
diff --git a/doc/dir-spec.txt b/doc/dir-spec.txt
index b97eb7fe94..a211ebc095 100644
--- a/doc/dir-spec.txt
+++ b/doc/dir-spec.txt
@@ -1,28 +1,26 @@
$Id$
- Tor directory protocol for 0.1.1.x series
+ Tor directory protocol, version 2
0. Scope and preliminaries
- This document should eventually be merged to replace and supplement the
- existing notes on directories in tor-spec.txt.
+ This directory protocol is used by Tor version 0.1.1.x and later. See
+ dir-spec-v1.txt for information on earlier versions.
- This is not a finalized version; what we actually wind up implementing
- may be different from the system described here.
+0.1. Goals and motivation
-0.1. Goals
-
- There are several problems with the way Tor handles directory information
+ There were several problems with the way Tor handles directory information
in version 0.1.0.x and earlier. Here are the problems we try to fix with
- this new design, already partially implemented in 0.1.1.x:
- 1. Directories are very large and use up a lot of bandwidth: clients
- download descriptors for all router several times an hour.
- 2. Every directory authority is a trust bottleneck: if a single
- directory authority lies, it can make clients believe for a time an
+ this new design, already implemented in 0.1.1.x:
+ 1. Directories were very large and use up a lot of bandwidth: clients
+ downloaded descriptors for all router several times an hour.
+ 2. Every directory authority was a trust bottleneck: if a single
+ directory authority lied, it could make clients believe for a time an
arbitrarily distorted view of the Tor network.
3. Our current "verified server" system is kind of nonsensical.
- 4. Getting more directory authorities adds more points of failure and
- worsens possible partitioning attacks.
+
+ 4. Getting more directory authorities would add more points of failure
+ and worsen possible partitioning attacks.
There are two problems that remain unaddressed by this design.
5. Requiring every client to know about every router won't scale.
@@ -82,9 +80,43 @@ $Id$
Routers used to upload fresh descriptors all the time, whether their keys
and other information had changed or not.
-2. Router operation
+1.2. Document meta-format
+
+ Router descriptors, directories, and running-routers documents all obey the
+ following lightweight extensible information format.
+
+ The highest level object is a Document, which consists of one or more
+ Items. Every Item begins with a KeywordLine, followed by one or more
+ Objects. A KeywordLine begins with a Keyword, optionally followed by
+ whitespace and more non-newline characters, and ends with a newline. A
+ Keyword is a sequence of one or more characters in the set [A-Za-z0-9-].
+ An Object is a block of encoded data in pseudo-Open-PGP-style
+ armor. (cf. RFC 2440)
+
+ More formally:
+
+ Document ::= (Item | NL)+
+ Item ::= KeywordLine Object*
+ KeywordLine ::= Keyword NL | Keyword WS ArgumentsChar+ NL
+ Keyword = KeywordChar+
+ KeywordChar ::= 'A' ... 'Z' | 'a' ... 'z' | '0' ... '9' | '-'
+ ArgumentChar ::= any printing ASCII character except NL.
+ WS = (SP | TAB)+
+ Object ::= BeginLine Base-64-encoded-data EndLine
+ BeginLine ::= "-----BEGIN " Keyword "-----" NL
+ EndLine ::= "-----END " Keyword "-----" NL
- The router descriptor format is unchanged from tor-spec.txt.
+ The BeginLine and EndLine of an Object must use the same keyword.
+
+ When interpreting a Document, software MUST reject any document containing a
+ KeywordLine that starts with a keyword it doesn't recognize.
+
+ The "opt" keyword is reserved for non-critical future extensions. All
+ implementations MUST ignore any item of the form "opt keyword ....." when
+ they would not recognize "keyword ....."; and MUST treat "opt keyword ....."
+ as synonymous with "keyword ......" when keyword is recognized.
+
+2. Router operation
ORs SHOULD generate a new router descriptor whenever any of the
following events have occurred:
@@ -105,6 +137,159 @@ $Id$
http://<hostname:port>/tor/
+2.1. Router descriptor format
+
+ Every router descriptor MUST start with a "router" Item; MUST end with a
+ "router-signature" Item and an extra NL; and MUST contain exactly one
+ instance of each of the following Items: "published" "onion-key"
+ "link-key" "signing-key" "bandwidth". Additionally, a router descriptor
+ MAY contain any number of "accept", "reject", "fingerprint", "uptime", and
+ "opt" Items. Other than "router" and "router-signature", the items may
+ appear in any order.
+
+ The items' formats are as follows:
+ "router" nickname address ORPort SocksPort DirPort
+
+ Indicates the beginning of a router descriptor. "address" must be an
+ IPv4 address in dotted-quad format. The last three numbers indicate
+ the TCP ports at which this OR exposes functionality. ORPort is a port
+ at which this OR accepts TLS connections for the main OR protocol;
+ SocksPort is deprecated and should always be 0; and DirPort is the
+ port at which this OR accepts directory-related HTTP connections. If
+ any port is not supported, the value 0 is given instead of a port
+ number.
+
+ "bandwidth" bandwidth-avg bandwidth-burst bandwidth-observed
+
+ Estimated bandwidth for this router, in bytes per second. The
+ "average" bandwidth is the volume per second that the OR is willing to
+ sustain over long periods; the "burst" bandwidth is the volume that
+ the OR is willing to sustain in very short intervals. The "observed"
+ value is an estimate of the capacity this server can handle. The
+ server remembers the max bandwidth sustained output over any ten
+ second period in the past day, and another sustained input. The
+ "observed" value is the lesser of these two numbers.
+
+ "platform" string
+
+ A human-readable string describing the system on which this OR is
+ running. This MAY include the operating system, and SHOULD include
+ the name and version of the software implementing the Tor protocol.
+
+ "published" YYYY-MM-DD HH:MM:SS
+
+ The time, in GMT, when this descriptor was generated.
+
+ "fingerprint"
+
+ A fingerprint (a HASH_LEN-byte of asn1 encoded public key, encoded in
+ hex, with a single space after every 4 characters) for this router's
+ identity key. A descriptor is considered invalid (and MUST be
+ rejected) if the fingerprint line does not match the public key.
+
+ [We didn't start parsing this line until Tor 0.1.0.6-rc; it should
+ be marked with "opt" until earlier versions of Tor are obsolete.]
+
+ "hibernating" 0|1
+
+ If the value is 1, then the Tor server was hibernating when the
+ descriptor was published, and shouldn't be used to build circuits.
+
+ [We didn't start parsing this line until Tor 0.1.0.6-rc; it should be
+ marked with "opt" until earlier versions of Tor are obsolete.]
+
+ "uptime"
+
+ The number of seconds that this OR process has been running.
+
+ "onion-key" NL a public key in PEM format
+
+ This key is used to encrypt EXTEND cells for this OR. The key MUST be
+ accepted for at least XXXX hours after any new key is published in a
+ subsequent descriptor.
+
+ "signing-key" NL a public key in PEM format
+
+ The OR's long-term identity key.
+
+ "accept" exitpattern
+ "reject" exitpattern
+
+ These lines, in order, describe the rules that an OR follows when
+ deciding whether to allow a new stream to a given address. The
+ 'exitpattern' syntax is described below.
+
+ "router-signature" NL Signature NL
+
+ The "SIGNATURE" object contains a signature of the PKCS1-padded
+ hash of the entire router descriptor, taken from the beginning of the
+ "router" line, through the newline after the "router-signature" line.
+ The router descriptor is invalid unless the signature is performed
+ with the router's identity key.
+
+ "contact" info NL
+
+ Describes a way to contact the server's administrator, preferably
+ including an email address and a PGP key fingerprint.
+
+ "family" names NL
+
+ 'Names' is a whitespace-separated list of server nicknames. If two
+ ORs list one another in their "family" entries, then OPs should treat
+ them as a single OR for the purpose of path selection.
+
+ For example, if node A's descriptor contains "family B", and node B's
+ descriptor contains "family A", then node A and node B should never
+ be used on the same circuit.
+
+ "read-history" YYYY-MM-DD HH:MM:SS (NSEC s) NUM,NUM,NUM,NUM,NUM... NL
+ "write-history" YYYY-MM-DD HH:MM:SS (NSEC s) NUM,NUM,NUM,NUM,NUM... NL
+
+ Declare how much bandwidth the OR has used recently. Usage is divided
+ into intervals of NSEC seconds. The YYYY-MM-DD HH:MM:SS field
+ defines the end of the most recent interval. The numbers are the
+ number of bytes used in the most recent intervals, ordered from
+ oldest to newest.
+
+ [We didn't start parsing these lines until Tor 0.1.0.6-rc; they should
+ be marked with "opt" until earlier versions of Tor are obsolete.]
+
+ "eventdns" bool NL
+
+ Declare whether this version of Tor is using the newer enhanced
+ dns logic. Versions of Tor without eventdns SHOULD not be used for
+ reverse hostname lookups.
+
+ [All versions of Tor before 0.1.2.2-alpha should be assumed to have
+ this option set to 0 if it is not present. All Tor versions at
+ 0.1.2.2-alpha or later should be assumed to have this option set to
+ 1 if it is not present. Until 0.1.2.1-alpha-dev, this option was
+ not generated, even when eventdns was in use. Versions of Tor
+ before 0.1.2.1-alpha-dev did not parse this option, so it should be
+ marked "opt". With some future version, the old 'dnsworker' logic
+ will be removed, rendering this option of historical interest only.]
+
+2.1. Nonterminals in router descriptors
+
+ nickname ::= between 1 and 19 alphanumeric characters, case-insensitive.
+
+ exitpattern ::= addrspec ":" portspec
+ portspec ::= "*" | port | port "-" port
+ port ::= an integer between 1 and 65535, inclusive.
+ addrspec ::= "*" | ip4spec | ip6spec
+ ipv4spec ::= ip4 | ip4 "/" num_ip4_bits | ip4 "/" ip4mask
+ ip4 ::= an IPv4 address in dotted-quad format
+ ip4mask ::= an IPv4 mask in dotted-quad format
+ num_ip4_bits ::= an integer between 0 and 32
+ ip6spec ::= ip6 | ip6 "/" num_ip6_bits
+ ip6 ::= an IPv6 address, surrounded by square brackets.
+ num_ip6_bits ::= an integer between 0 and 128
+
+ bool ::= "0" | "1"
+
+ Ports are required; if they are not included in the router
+ line, they must appear in the "ports" lines.
+
3. Network status format
Directory authorities generate, sign, and compress network-status
@@ -183,13 +368,15 @@ $Id$
"V2Dir" if the router implements this protocol.
The "r" entry for each router must appear first and is required. The
- 's" entry is optional. Unrecognized flags and extra elements on the
- "r" line must be ignored.
+ "s" entry is optional. Unrecognized flags on the "s" line and extra
+ elements on the "r" line must be ignored.
The signature section contains:
- "directory-signature". A signature of the rest of the document using
- the directory authority's signing key.
+ "directory-signature". A signature of the rest of the document
+ (the document up until the signature, including the line
+ "directory-signature <nick>\n") using the directory authority's
+ signing key.
We compress the network status list with zlib before transmitting it.
@@ -386,7 +573,7 @@ $Id$
5. Client operation: downloading information
- Every Tor that is not a directory server (that is, clients and ORs that do
+ Every Tor that is not a directory server (that is, those that do
not have a DirPort set) implements this section.
5.1. Downloading network-status documents
@@ -591,7 +778,7 @@ $Id$
consecutive times) a network-status for every authority, and at
least one of the authorities is "Naming", and no live "Naming"
authorities publish a binding for the router's nickname, the
- router SHOULD remind the operator that the chosen nickname is not
+ router MAY remind the operator that the chosen nickname is not
bound to this key at the authorities, and suggest contacting the
authority operators.
diff --git a/doc/incentives.txt b/doc/incentives.txt
index 5b805549d1..994310025d 100644
--- a/doc/incentives.txt
+++ b/doc/incentives.txt
@@ -177,7 +177,7 @@
Addendum: I was more thinking of measuring based on who is the service
provider and service receiver for the circuit. Say Alice builds a
circuit to Bob. Then Bob is providing service to Alice, since he
- otherwise wouldn't need to spend is bandwidth. So traffic in either
+ otherwise wouldn't need to spend his bandwidth. So traffic in either
direction should be charged to Alice. Of course, the same attack would
work, namely, Bob could cheat by sending bytes back quickly. So someone
close to the origin needs to detect this and close the circuit, if
diff --git a/doc/path-spec.txt b/doc/path-spec.txt
new file mode 100644
index 0000000000..fde40f4299
--- /dev/null
+++ b/doc/path-spec.txt
@@ -0,0 +1,551 @@
+$Id$
+
+ Tor Path Specification
+
+ Roger Dingledine
+ Nick Mathewson
+
+Note: This is an attempt to specify Tor as currently implemented. Future
+versions of Tor will implement improved algorithms.
+
+This document tries to cover how Tor chooses to build circuits and assign
+streams to circuits. Other implementations MAY take other approaches, but
+implementors should be aware of the anonymity and load-balancing implications
+of their choices.
+
+ THIS SPEC ISN'T DONE OR CORRECT.
+I'm just copying in relevant info so far. The starred points are things we
+should cover, but not an exhaustive list. -NM
+
+1. General operation
+
+ Tor begins building circuits as soon as it has enough directory
+ information to do so (see section 5.1 of dir-spec.txt). Some circuits are
+ built preemptively because we expect to need them later (for user
+ traffic), and some are build because of immediate need (for user traffic
+ that no current circuit can handle, for testing the network or our
+ availability, and so on).
+
+ When a client application creates a new stream (by opening a SOCKS
+ connection or launching a resolve request), we attach it to an appropriate
+ open (or in-progress) circuit if one exists, and launch a new circuit only
+ if no current circuit can handle the request. We rotate circuits over
+ time to avoid some profiling attacks.
+
+ To build a circuit, we choose all the nodes we want to use, and then
+ construct the circuit. Sometimes, when we want a circuit that ends at a
+ given hop, and we have an appropriate unused circuit, we "cannibalize" the
+ existing circuit and extend it to the new terminus.
+
+ These processes are described in more detail below.
+
+ This document describes Tor's automatic path selection logic only; path
+ selection can be overridden by a controller (with the EXTENDCIRCUIT and
+ ATTACHSTREAM commands). Paths constructed through these means will
+ violate some constraints given below.
+
+1b. Types of circuits.
+
+* Stable / Ordinary
+* Internal / Exit
+
+ XXXX
+
+1c. Terminology
+
+ A "path" is an ordered sequence of nodes, not yet built as a circuit.
+
+ A "clean" circuit is one that has not yet been used for any traffic.
+
+ A "stable" node is one that we believe to have the 'Stable' flag set on
+ the basis of our current directory information. A "stable" circuit is one
+ that consists entirely of "stable" nodes.
+
+ A "fast" or "stable" node is one that we believe to have the 'Fast' or
+ 'Stable' flag set on the basis of our current directory information. A
+ "fast" or "stable" circuit is one consisting only of "fast" or "stable"
+ nodes.
+
+ A "request" is a client-side connection or DNS resolve that needs to be
+ served by a circuit.
+
+ A "pending" circuit is one that we have started to build, but which has
+ not yet completed.
+
+ A circuit or path "supports" a request if it is okay to use the
+ circuit/path to fulfill the request, according to the rules given below.
+ A circuit or path "might support" a request if some aspect of the request
+ is unknown (usually its target IP), but we believe the path probably
+ supports the request according to the rules given below.
+
+2. Building circuits
+
+2.1. When we build.
+
+2.1.1. When clients build circuits
+
+ When running as a client, Tor tries to maintain at least 3 clean circuits,
+ so that new streams can be handled quickly. To increase the likelihood of
+ success, Tor tries to predict what exit nodes will be useful by choosing
+ from among nodes that support the ports we have used in the recent past.
+ (see 2.4). [XXXX describe in detail how predicted ports work.]
+
+ Additionally, when a client request exists that no circuit (built or
+ pending) might support, we cannibalize an existing circuit (2.1.4) or
+ create a new circuit to support the request. We do so by picking a
+ request at random, building or cannibalizing a circuit to support it, and
+ repeating until every unattached request might be supported by a pending
+ or built circuit.
+
+ XXXX when long idle, we build nothing.
+
+2.1.2. When servers build circuits
+
+ XXXX
+
+2.1.3. When authorities build circuits
+
+ XXXX
+
+2.1.4. Hidden-service circuits
+
+ See section 4 below.
+
+2.1.4. Cannibalizing circuits
+
+ When Tor has a request (either an unattached stream or unattached resolve
+ request) that no current circuit can support, it looks for an existing
+ clean circuit to cannibalize. If it finds one, it tries to extend it
+ another hop to an exit node that might support the stream. [Must be
+ internal???]
+
+ If no circuit exists, or is currently being built, along a path that
+ might support a stream, we begin building a new circuit that might support
+ the stream.
+
+ [XXXX always? really?]
+
+2.2. Path selection and constraints
+
+ We choose the path for each new circuit before we build it. We choose the
+ exit node first, followed by the other nodes in the circuit. All paths
+ we generate obey the following constraints:
+ - We do not choose the same router twice for the same path.
+ - We do not choose any router in the same family as another in the same
+ circuit.
+ - We do not choose any router in the same /16 subnet as another in the
+ same circuit.
+ - We don't choose any non-running or non-valid router unless we have
+ been configured to do so.
+ - If we're using Guard nodes, the first node must be a Guard (see 5
+ below)
+ - XXXX Choosing the length
+
+ When choosing among multiple candidates for a path element, we choose
+ a given router with probability proportional to its advertised bandwidth
+ [the smaller of the 'rate' and 'observed' arguments to the "bandwidth"
+ element in its descriptor]. If a router's advertised bandwidth is greater
+ than MAX_BELIEVEABLE_BANDWIDTH (1.5 MB/sec), we clip to that value.
+
+ (XXXX We should do something to shift traffic away from exit nodes.)
+
+ Additionally, we may be building circuits with one or more requests in
+ mind. Each kind of request puts certain constraints on paths:
+
+ - All service-side introduction circuits and all rendezvous paths
+ should be Stable.
+ - All connection requests for connections that we think will need to
+ stay open a long time require Stable circuits. Currently, Tor decides
+ this by examining the request's target port, and comparing it to a
+ list of "long-lived" ports. (Default: 21, 22, 706, 1863, 5050, 5190,
+ 5222, 5223, 6667, 8300, 8888.)
+ - DNS resolves require an exit node whose exit policy is not equivalent
+ to "reject *:*".
+ - Reverse DNS resolves require a version of Tor with advertised eventdns
+ support, running 0.1.2.1-alpha-dev or later.
+ - All connection requests require an exit node whose exit policy
+ supports their target address and port (if known), or which "might
+ support it" (if the address isn't known). See 2.2.1.
+ - Rules for Fast? XXXXX
+
+2.2.1. Choosing an exit
+
+ If we know what IP we want to resolve, we can trivially tell whether a
+ given router will support it by simulating its declared exit policy.
+
+ Because we often connect to addresses of the form hostname:port, we do not
+ always know the target IP address when we select an exit node. In these
+ cases, we need to pick an exit node that "might support" connections to a
+ given address port with an unknown address. An exit node "might support"
+ such a connection if any clause that accepts any connections to that port
+ precedes all clauses (if any) that reject all connections to that port.
+
+2.2.2. User configuration
+
+ Users can alter the default behavior for path selection with configuration
+ options.
+
+ - If "ExitNodes" is provided, then every request requires an exit node on
+ the ExitNodes list. (If a request is supported by no nodes on that list,
+ and StrictExitNodes is false, then Tor treats that request as if
+ ExitNodes were not provided.)
+
+ - "EntryNodes" and "StrictEntryNodes" behave analagously.
+
+ - If a user tries to connect to or resolve a hostname of the form
+ <target>.<servername>.exit, the request is rewritten to a request for
+ <target>, and the request is only supported by the exit whose nickname
+ or fingerprint is <servername>.
+
+2.3. Handling failure
+
+ If an attempt to extend a circuit fails (either because the first create
+ failed or a subsequent extend failed) then the circuit is torn down and is
+ no longer pending. (XXXX really?) Requests that might have been
+ supported by the pending circuit thus become unsupported, and a new
+ circuit needs to be constructed.
+
+ If we fail to being a circuit with an EXITPOLICY error, we decide that the
+ exit node's exit policy is not correctly advertised, so we treat the exit
+ node as if it were a non-exit until we retrieve a fresh descriptor for it.
+
+ XXXX
+
+2.4. Tracking "predicted" ports
+
+ A Tor client tracks how much time has passed since it last received a
+ request for a connection on each port. (For the purposes of this section,
+ requests for hostname resolves are considered requests to a separate
+ port). Tor forgets about ports that haven't been used for an hour
+ [PREDICTED_CIRCS_RELEVANCE_TIME].
+
+ The ports that have been used in the last hour are considered "predicted",
+ and Tor will try to maintain a clean circuits to them as described in 2.1.
+
+ For bootstrapping purposes, port 80 is treated as used at startup time.
+
+ Tor clients SHOULD NOT store predicted ports to a persistent medium.
+
+3. Attaching streams to circuits
+
+ When a circuit that might support a request is built, Tor tries to attach
+ the request's stream to the circuit and sends a BEGIN or RESOLVE relay
+ cell as appropriate. If the request completes unsuccessfully, Tor
+ considers the reason given in the CLOSE relay cell. [XXX yes, and?]
+
+
+ After a request has remained unattached for [XXXX retries? interval?], Tor
+ abandons the attempt and signals an error to the client as appropriate
+ (e.g., by closing the SOCKS connection).
+
+ XXX Timeouts and when Tor auto-retries.
+ * What stream-end-reasons are appropriate for retrying.
+
+ XXX What if no reply to BEGIN/RESOLVE?
+
+4. Hidden-service related circuits
+
+ XXX Tracking expected hidden service use (client-side and hidserv-side)
+
+5. Guard nodes
+
+ XXX writeme
+
+6. Testing circuits
+
+
+
+
+(From some emails by arma)
+
+Hi folks,
+
+I've gotten the codebase to the point that I'm going to start trying
+to make helper nodes work well. With luck they will be on by default in
+the final 0.1.1.x release.
+
+For background on helper nodes, read
+http://wiki.noreply.org/noreply/TheOnionRouter/TorFAQ#RestrictedEntry
+
+First order of business: the phrase "helper node" sucks. We always have
+to define it after we say it to somebody. Nick likes the phrase "contact
+node", because they are your point-of-contact into the network. That is
+better than phrases like "bridge node". The phrase "fixed entry node"
+doesn't seem to work with non-math people, because they wonder what was
+broken about it. I'm sort of partial to the phrase "entry node" or maybe
+"restricted entry node". In any case, if you have ideas on names, please
+mail me off-list and I'll collate them.
+
+Right now the code exists to pick helper nodes, store our choices to
+disk, and use them for our entry nodes. But there are three topics
+to tackle before I'm comfortable turning them on by default. First,
+how to handle churn: since Tor nodes are not always up, and sometimes
+disappear forever, we need a plan for replacing missing helpers in a
+safe way. Second, we need a way to distinguish "the network is down"
+from "all my helpers are down", also in a safe way. Lastly, we need to
+examine the situation where a client picks three crummy helper nodes
+and is forever doomed to a lousy Tor experience. Here's my plan:
+
+How to handle churn.
+ - Keep track of whether you have ever actually established a
+ connection to each helper. Any helper node in your list that you've
+ never used is ok to drop immediately. Also, we don't save that
+ one to disk.
+ - If all our helpers are down, we need more helper nodes: add a new
+ one to the *end*of our list. Only remove dead ones when they have
+ been gone for a very long time (months).
+ - Pick from the first n (by default 3) helper nodes in your list
+ that are up (according to the network-statuses) and reachable
+ (according to your local firewall config).
+ - This means that order matters when writing/reading them to disk.
+
+How to deal with network down.
+ - While all helpers are down/unreachable and there are no established
+ or on-the-way testing circuits, launch a testing circuit. (Do this
+ periodically in the same way we try to establish normal circuits
+ when things are working normally.)
+ (Testing circuits are a special type of circuit, that streams won't
+ attach to by accident.)
+ - When a testing circuit succeeds, mark all helpers up and hold
+ the testing circuit open.
+ - If a connection to a helper succeeds, close all testing circuits.
+ Else mark that helper down and try another.
+ - If the last helper is marked down and we already have a testing
+ circuit established, then add the first hop of that testing circuit
+ to the end of our helper node list, close that testing circuit,
+ and go back to square one. (Actually, rather than closing the
+ testing circuit, can we get away with converting it to a normal
+ circuit and beginning to use it immediately?)
+
+How to pick non-sucky helpers.
+ - When we're picking a new helper nodes, don't use ones which aren't
+ reachable according to our local ReachableAddresses configuration.
+ (There's an attack here: if I pick my helper nodes in a very
+ restrictive environment, say "ReachableAddresses 18.0.0.0/255.0.0.0:*",
+ then somebody watching me use the network from another location will
+ guess where I first joined the network. But let's ignore it for now.)
+ - Right now we choose new helpers just like we'd choose any entry
+ node: they must be "stable" (claim >1day uptime) and "fast" (advertise
+ >10kB capacity). In 0.1.1.11-alpha, clients let dirservers define
+ "stable" and "fast" however they like, and they just believe them.
+ So the next step is to make them a function of the current network:
+ e.g. line up all the 'up' nodes in order and declare the top
+ three-quarter to be stable, fast, etc, as long as they meet some
+ minimum too.
+ - If that's not sufficient (it won't be), dirservers should introduce
+ a new status flag: in additional to "stable" and "fast", we should
+ also describe certain nodes as "entry", meaning they are suitable
+ to be chosen as a helper. The first difference would be that we'd
+ demand the top half rather than the top three-quarters. Another
+ requirement would be to look at "mean time between returning" to
+ ensure that these nodes spend most of their time available. (Up for
+ two days straight, once a month, is not good enough.)
+ - Lastly, we need a function, given our current set of helpers and a
+ directory of the rest of the network, that decides when our helper
+ set has become "too crummy" and we need to add more. For example,
+ this could be based on currently advertised capacity of each of
+ our helpers, and it would also be based on the user's preferences
+ of speed vs. security.
+
+***
+
+Lasse wrote:
+> I am a bit concerned with performance if we are to have e.g. two out of
+> three helper nodes down or unreachable. How often should Tor check if
+> they are back up and running?
+
+Right now Tor believes a threshold of directory servers when deciding
+whether each server is up. When Tor observes a server to be down
+(connection failed or building the first hop of the circuit failed),
+it marks it as down and doesn't try it again, until it gets a new
+network-status from somebody, at which point it takes a new concensus
+and marks the appropriate servers as up.
+
+According to sec 5.1 of dir-spec.txt, the client will try to fetch a new
+network-status at least every 30 minutes, and more often in certain cases.
+
+With the proposed scheme, we'll also mark all our helpers as up shortly
+after the last one is marked down.
+
+> When should there be
+> added an extra node to the helper node list? This is kind of an
+> important threshold?
+
+I agree, this is an important question. I don't have a good answer yet. Is
+it terrible, anonymity-wise, to add a new helper every time only one of
+your helpers is up? Notice that I say add rather than replace -- so you'd
+only use this fourth helper when one of your main three helpers is down,
+and if three of your four are down, you'd add a fifth, but only use it
+when two of the first four are down, etc.
+
+In fact, this may be smarter than just picking a random node for your
+testing circuit, because if your network goes up and down a lot, then
+eventually you have a chance of using any entry node in the network for
+your testing circuit.
+
+We have a design choice here. Do we only try to use helpers for the
+connections that will have streams on them (revealing our communication
+partners), or do we also want to restrict the overall set of nodes that
+we'll connect to, to discourage people from enumerating all Tor clients?
+
+I'm increasingly of the belief that we want to hide our presence too,
+based on the fact that Steven and George and others keep coming up with
+attacks that start with "Assuming we know the set of users".
+
+If so, then here's a revised "How to deal with network down" section:
+
+ 1) When a helper is marked down or the helper list shrinks, and as
+ a result the total number of helpers that are either (up and
+ reachable) or (reachable but never connected to) is <= 1, then pick
+ a new helper and add it to the end of the list.
+ [We count nodes that have never been connected to, since otherwise
+ we might keep on adding new nodes before trying any of them. By
+ "reachable" I mean "is allowed by ReachableAddresses".]
+ 2) When you fail to connect to a helper that has never been connected
+ to, you remove him from the list right then (and the above rule
+ might kick in).
+ 3) When you succeed at connecting to a helper that you've never
+ connected to before, mark all reachable helpers earlier in the list
+ as up, and close that circuit.
+ [We close the circuit, since if the other helpers are now up, we
+ prefer to use them for circuits that will reveal communication
+ partners.]
+
+This certainly seems simpler. Are there holes that I'm missing?
+
+> If running from a laptop you will meet different firewall settings, so
+> how should Helper Nodes settings keep up with moving from an open
+> ReachableAddresses to a FascistFirewall setting after the helper nodes
+> have been selected?
+
+I added the word "reachable" to three places in the above list, and I
+believe that totally solves this question.
+
+And as a bonus, it leads to an answer to Nick's attack ("If I pick
+my helper nodes all on 18.0.0.0:*, then I move, you'll know where I
+bootstrapped") -- the answer is to pick your original three helper nodes
+without regard for reachability. Then the above algorithm will add some
+more that are reachable for you, and if you move somewhere, it's more
+likely (though not certain) that some of the originals will become useful.
+Is that smart or just complex?
+
+> What happens if(when?) performance of the third node is bad?
+
+My above solution solves this a little bit, in that we always try to
+have two nodes available. But what if they are both up but bad? I'm not
+sure. As my previous mail said, we need some function, given our list
+of helpers and the network directory, that will tell us when we're in a
+bad situation. I can imagine some simple versions of this function --
+for example, when both our working helpers are in the bottom half of
+the nodes, ranked by capacity.
+
+But the hard part: what's the remedy when we decide there's something
+to fix? Do we add a third, and now we have two crummy ones and a new
+one? Or do we drop one or both of the bad ones?
+
+Perhaps we believe the latest claim from the network-status concensus,
+and we count a helper the dirservers believe is crummy as "not worth
+trying" (equivalent to "not reachable under our current ReachableAddresses
+config") -- and then the above algorithm would end up adding good ones,
+but we'd go back to the originals if they resume being acceptable? That's
+an appealing design. I wonder if it will cause the typical Tor user to
+have a helper node list that comprises most of the network, though. I'm
+ok with this.
+
+> Another point you might want to keep in mind, is the possibility to
+> reuse the code in order to add a second layer helper node (meaning node
+> number two) to "protect" the first layer (node number one) helper nodes.
+> These nodes should be tied to each of the first layer nodes. E.g. there
+> is one helper node list, as described in your mail, for each of the
+> first layer nodes, following their create/destroy.
+
+True. Does that require us to add a fourth hop to our path length,
+since the first hop is from a limited set, the second hop is from a
+limited set, and the third hop might also be constrained because, say,
+we're asking for an unusual exit port?
+
+> Another of the things might worth adding to the to do list is
+> localization of server (helper) nodes. Making it possible to pick
+> countries/regions where you do (not) want your helper nodes located. (As
+> in "HelperNodesLocated us,!eu" etc.) I know this requires the use of
+> external data and may not be worth it, but it _could_ be integrated at
+> the directory servers only -- adding a list of node IP's and e.g. a
+> country/region code to the directory and thus reduce the overhead. (?)
+> Maybe extending the Family-term?
+
+I think we are heading towards doing path selection based on geography,
+but I don't have a good sense yet of how that will actually turn out --
+that is, with what mechanism Tor clients will learn the information they
+need. But this seems to be something that is orthogonal to the rest of
+this discussion, so I look forward to having somebody else solve it for
+us, and fitting it in when it's ready. :)
+
+> And I would like to keep an option to pick the first X helper nodes
+> myself and then let Tor extend this list if these nodes are down (like
+> EntryNodes in current code). Even if this opens up for some new types of
+> "relationship" attacks.
+
+Good idea. Here's how I'd like to name these:
+
+The "EntryNodes" config option is a list of seed helper nodes. When we
+read EntryNodes, any node listed in entrynodes but not in the current
+helper node list gets *pre*pended to the helper node list.
+
+The "NumEntryNodes" config option (currently called NumHelperNodes)
+specifies the number of up, reachable, good-enough helper nodes that
+will make up the pool of possible choices for first hop, counted from
+the front of the helper node list until we have enough.
+
+The "UseEntryNodes" config option (currently called UseHelperNodes)
+tells us to turn on all this helper node behavior. If you set EntryNodes,
+then this option is implied.
+
+The "StrictEntryNodes" config option, provided for backward compatibility
+and for debugging, means a) we replace the helper node list with the
+current EntryNodes list, and b) whenever we would do an operation that
+alters the helper node list, we don't. (Yes, this means that if all the
+helper nodes are down, we lose until we mark them up again. But this is
+how it behaves now.)
+
+> I am sure my next point has been asked before, but what about testing
+> the current speed of the connections when looking for new helper nodes,
+> not only testing the connectivity? I know this might contribute to a lot
+> of overhead in the network, but if this only occur e.g. when using
+> helper nodes as a Hidden Service it might not have that large an impact,
+> but could help availability for the services?
+
+If we're just going to be testing them when we're first picking them,
+then it seems we can do the same thing by letting the directory servers
+test them. This has the added benefit that all the (behaving) clients
+use the same data, so they don't end up partitioned by a node that
+(for example) performs selectively for his victims.
+
+Another idea would be to periodically keep track of what speeds you get
+through your helpers, and make decisions from this. The reason we haven't
+done this yet is because there are a lot of variables -- perhaps the
+web site is slow, perhaps some other node in the path is slow, perhaps
+your local network is slow briefly, perhaps you got unlucky, etc. I
+believe that over time (assuming the user has roughly the same browsing
+habits) all of these would average out and you'd get a usable answer,
+but I don't have a good sense of how long it would take to converge,
+so I don't know whether this would be worthwhile.
+
+> BTW. I feel confortable with all the terms helper/entry/contact nodes,
+> but I think you (the developers) should just pick one and stay with it
+> to avoid confusion.
+
+I think I'm going to try to co-opt the term 'Entry' node for this
+purpose. We're going to have to keep referring to helper nodes for the
+research community for a while though, so they realize that Tor does
+more than just let users ask for certain entry nodes.
+
+
+
+============================================================
+Some stuff that worries me about entry guards. 2006 Jun, Nickm.
+
+1. It is unlikely for two users to have the same set of entry guards.
+
+2. Observing a user is sufficient to learn its entry guards.
+
+3. So, as we move around, we leak our
diff --git a/doc/socks-extensions.txt b/doc/socks-extensions.txt
index 7022af14fc..8040a8b03f 100644
--- a/doc/socks-extensions.txt
+++ b/doc/socks-extensions.txt
@@ -46,6 +46,12 @@ Tor's extensions to the SOCKS protocol
(We support RESOLVE in SOCKS4 too, even though it is unnecessary.)
+ For SOCKS5 only, we support reverse resolution with a new command value,
+ "RESOLVE_PTR". In response to a "RESOLVE_PTR" SOCKS5 command with an IPv4
+ address as its target, Tor attempts to find the canonical hostname for that
+ IPv4 record, and returns it in the "server bound address" portion of the
+ reply. (This was not supported before Tor 0.1.2.2-alpha)
+
3. HTTP-resistance
Tor checks the first byte of each SOCKS request to see whether it looks
diff --git a/doc/tor-osx-dmg-creation.txt b/doc/tor-osx-dmg-creation.txt
index 95a0414f89..c7db886b4a 100644
--- a/doc/tor-osx-dmg-creation.txt
+++ b/doc/tor-osx-dmg-creation.txt
@@ -1,3 +1,6 @@
+## Instructions for building the official dmgs for OSX.
+##
+
The following steps are the exact steps used to produce the "official"
OSX builds of tor
diff --git a/doc/tor-rpm-creation.txt b/doc/tor-rpm-creation.txt
index 9af2556b79..9597cca5f6 100644
--- a/doc/tor-rpm-creation.txt
+++ b/doc/tor-rpm-creation.txt
@@ -1,13 +1,15 @@
-These are instructions for building Tor binaries in the rpm format on
+## Instructions for building the official rpms.
+##
+These are instructions for building Tor binaries in the rpm format on
various cpu architectures and operating systems. Each rpm will require
-glibc on the target system. It is believed that any rpm-based linux
-distribution should have semi-current glibc installed by default.
-If you run into a distribution that does not work with glibc, or does
-not contain it, please let us know the details.
+glibc on the target system. It is believed that any rpm-based linux
+distribution should have semi-current glibc installed by default.
+If you run into a distribution that does not work with glibc, or does
+not contain it, please let us know the details.
-These are the exact same steps used to build the official rpms of Tor.
+These are the exact steps used to build the official rpms of Tor.
-If you wish to further tune Tor binaries in rpm format beyond this list,
+If you wish to further tune Tor binaries in rpm format beyond this list,
see the GCC doc page for further options:
http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/
@@ -23,14 +25,13 @@ this:
%define target_cpu i386
%define target_os linux
-
The three parameters: target, target_cpu, and target_os are used
throughout the "make dist-rpm" process. They control the parameters
-passed to "configure" and the final tuning of the binaries produced.
+passed to "configure" and the final tuning of the binaries produced.
The default settings, as shown above, create binaries for the widest
-range of Intel x86 or compatible architectures.
+range of Intel x86 or x86-compatible architectures.
-The paramters can be set as follows:
+The parameters can be set as follows:
The "target" parameter:
This should be "gnu", "redhat", or the short name of your linux distribution.
@@ -39,7 +40,6 @@ Other possibilities are "mandrake" or "suse". This is passed to
Therefore, this "target" parameter must be a valid OS for "configure" as
well.
-
The "target_cpu" parameter:
This parameter controls the optimization and tuning of your binaries via
gcc and "configure". This parameter is passed to gcc via the -mtune= or
@@ -49,10 +49,7 @@ through the --host, --build, and --target parameters. Therefore, this
few common options for this parameter may be "athlon64, i686, pentium4" or
others.
-
The "target_os" parameter:
This parameter controls the target operating system. Normally, this is
only "linux". If you wish to build rpms for a non-linux operating
system, you can replace "linux" with your operating system.
-
-
diff --git a/doc/tor-spec-udp.txt b/doc/tor-spec-udp.txt
index 59d05342c0..9e4966c8c4 100644
--- a/doc/tor-spec-udp.txt
+++ b/doc/tor-spec-udp.txt
@@ -383,5 +383,32 @@ Other sections:
What changes need to happen to each node's exit policy to support this? -RD
+Switching to UDP means managing the queues of incoming packets better,
+so we don't miss packets. How does this interact with doing large public
+key operations (handshakes) in the same thread?
+========================================================================
+COMMENTS
+========================================================================
+[16 May 2006]
+
+I don't favor this approach; it makes packet traffic partitioned from
+stream traffic end-to-end. The architecture I'd like to see is:
+
+ A *All* Tor-to-Tor traffic is UDP/DTLS, unless we need to fall back on
+ TCP/TLS for firewall penetration or something. (This also gives us an
+ upgrade path for routing through legacy servers.)
+
+ B Stream traffic is handled with end-to-end per-stream acks/naks and
+ retries. On failure, the data is retransmitted in a new RELAY_DATA cell;
+ a cell isn't retransmitted.
+
+We'll need to do A anyway, to fix our behavior on packet-loss. Once we've
+done so, B is more or less inevitable, and we can support end-to-end UDP
+traffic "for free".
+
+(Also, there are some details that this draft spec doesn't address. For
+example, what happens when a UDP packet doesn't fit in a single cell?)
+
+-NM
diff --git a/doc/tor-spec-v0.txt b/doc/tor-spec-v0.txt
new file mode 100644
index 0000000000..d64647d7d8
--- /dev/null
+++ b/doc/tor-spec-v0.txt
@@ -0,0 +1,734 @@
+$Id$
+
+ Tor Protocol Specification
+
+ Roger Dingledine
+ Nick Mathewson
+
+Note: This document specifies Tor as currently implemented in versions
+0.1.2.1-alpha and earlier. Current protocol designs are described in
+tor-spec.txt.
+
+0. Preliminaries
+
+0.1. Notation and encoding
+
+ PK -- a public key.
+ SK -- a private key.
+ K -- a key for a symmetric cypher.
+
+ a|b -- concatenation of 'a' and 'b'.
+
+ [A0 B1 C2] -- a three-byte sequence, containing the bytes with
+ hexadecimal values A0, B1, and C2, in that order.
+
+ All numeric values are encoded in network (big-endian) order.
+
+ H(m) -- a cryptographic hash of m.
+
+0.2. Security parameters
+
+ Tor uses a stream cipher, a public-key cipher, the Diffie-Hellman
+ protocol, and a hash function.
+
+ KEY_LEN -- the length of the stream cipher's key, in bytes.
+
+ PK_ENC_LEN -- the length of a public-key encrypted message, in bytes.
+ PK_PAD_LEN -- the number of bytes added in padding for public-key
+ encryption, in bytes. (The largest number of bytes that can be encrypted
+ in a single public-key operation is therefore PK_ENC_LEN-PK_PAD_LEN.)
+
+ DH_LEN -- the number of bytes used to represent a member of the
+ Diffie-Hellman group.
+ DH_SEC_LEN -- the number of bytes used in a Diffie-Hellman private key (x).
+
+ HASH_LEN -- the length of the hash function's output, in bytes.
+
+ CELL_LEN -- The length of a Tor cell, in bytes.
+
+0.3. Ciphers
+
+ For a stream cipher, we use 128-bit AES in counter mode, with an IV of all
+ 0 bytes.
+
+ For a public-key cipher, we use RSA with 1024-bit keys and a fixed
+ exponent of 65537. We use OAEP padding, with SHA-1 as its digest
+ function. (For OAEP padding, see
+ ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf)
+
+ For Diffie-Hellman, we use a generator (g) of 2. For the modulus (p), we
+ use the 1024-bit safe prime from rfc2409, (section 6.2) whose hex
+ representation is:
+
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE65381FFFFFFFFFFFFFFFF"
+
+ As an optimization, implementations SHOULD choose DH private keys (x) of
+ 320 bits. Implementations that do this MUST never use any DH key more
+ than once.
+
+ For a hash function, we use SHA-1.
+
+ KEY_LEN=16.
+ DH_LEN=128; DH_GROUP_LEN=40.
+ PK_ENC_LEN=128; PK_PAD_LEN=42.
+ HASH_LEN=20.
+
+ When we refer to "the hash of a public key", we mean the SHA-1 hash of the
+ DER encoding of an ASN.1 RSA public key (as specified in PKCS.1).
+
+ All "random" values should be generated with a cryptographically strong
+ random number generator, unless otherwise noted.
+
+ The "hybrid encryption" of a byte sequence M with a public key PK is
+ computed as follows:
+ 1. If M is less than PK_ENC_LEN-PK_PAD_LEN, pad and encrypt M with PK.
+ 2. Otherwise, generate a KEY_LEN byte random key K.
+ Let M1 = the first PK_ENC_LEN-PK_PAD_LEN-KEY_LEN bytes of M,
+ and let M2 = the rest of M.
+ Pad and encrypt K|M1 with PK. Encrypt M2 with our stream cipher,
+ using the key K. Concatenate these encrypted values.
+ [XXX Note that this "hybrid encryption" approach does not prevent
+ an attacker from adding or removing bytes to the end of M. It also
+ allows attackers to modify the bytes not covered by the OAEP --
+ see Goldberg's PET2006 paper for details. We will add a MAC to this
+ scheme one day. -RD]
+
+0.4. Other parameter values
+
+ CELL_LEN=512
+
+1. System overview
+
+ Tor is a distributed overlay network designed to anonymize
+ low-latency TCP-based applications such as web browsing, secure shell,
+ and instant messaging. Clients choose a path through the network and
+ build a ``circuit'', in which each node (or ``onion router'' or ``OR'')
+ in the path knows its predecessor and successor, but no other nodes in
+ the circuit. Traffic flowing down the circuit is sent in fixed-size
+ ``cells'', which are unwrapped by a symmetric key at each node (like
+ the layers of an onion) and relayed downstream.
+
+2. Connections
+
+ There are two ways to connect to an onion router (OR). The first is
+ as an onion proxy (OP), which allows the OP to authenticate the OR
+ without authenticating itself. The second is as another OR, which
+ allows mutual authentication.
+
+ Tor uses TLS for link encryption. All implementations MUST support
+ the TLS ciphersuite "TLS_EDH_RSA_WITH_DES_192_CBC3_SHA", and SHOULD
+ support "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" if it is available.
+ Implementations MAY support other ciphersuites, but MUST NOT
+ support any suite without ephemeral keys, symmetric keys of at
+ least KEY_LEN bits, and digests of at least HASH_LEN bits.
+
+ An OP or OR always sends a two-certificate chain, consisting of a
+ certificate using a short-term connection key and a second, self-
+ signed certificate containing the OR's identity key. The commonName of the
+ first certificate is the OR's nickname, and the commonName of the second
+ certificate is the OR's nickname, followed by a space and the string
+ "<identity>".
+
+ All parties receiving certificates must confirm that the identity key is
+ as expected. (When initiating a connection, the expected identity key is
+ the one given in the directory; when creating a connection because of an
+ EXTEND cell, the expected identity key is the one given in the cell.) If
+ the key is not as expected, the party must close the connection.
+
+ All parties SHOULD reject connections to or from ORs that have malformed
+ or missing certificates. ORs MAY accept or reject connections from OPs
+ with malformed or missing certificates.
+
+ Once a TLS connection is established, the two sides send cells
+ (specified below) to one another. Cells are sent serially. All
+ cells are CELL_LEN bytes long. Cells may be sent embedded in TLS
+ records of any size or divided across TLS records, but the framing
+ of TLS records MUST NOT leak information about the type or contents
+ of the cells.
+
+ TLS connections are not permanent. An OP or an OR may close a
+ connection to an OR if there are no circuits running over the
+ connection, and an amount of time (KeepalivePeriod, defaults to 5
+ minutes) has passed.
+
+ (As an exception, directory servers may try to stay connected to all of
+ the ORs -- though this will be phased out for the Tor 0.1.2.x release.)
+
+3. Cell Packet format
+
+ The basic unit of communication for onion routers and onion
+ proxies is a fixed-width "cell". Each cell contains the following
+ fields:
+
+ CircID [2 bytes]
+ Command [1 byte]
+ Payload (padded with 0 bytes) [CELL_LEN-3 bytes]
+ [Total size: CELL_LEN bytes]
+
+ The CircID field determines which circuit, if any, the cell is
+ associated with.
+
+ The 'Command' field holds one of the following values:
+ 0 -- PADDING (Padding) (See Sec 6.2)
+ 1 -- CREATE (Create a circuit) (See Sec 4.1)
+ 2 -- CREATED (Acknowledge create) (See Sec 4.1)
+ 3 -- RELAY (End-to-end data) (See Sec 4.5 and 5)
+ 4 -- DESTROY (Stop using a circuit) (See Sec 4.4)
+ 5 -- CREATE_FAST (Create a circuit, no PK) (See Sec 4.1)
+ 6 -- CREATED_FAST (Circuit created, no PK) (See Sec 4.1)
+ 7 -- HELLO (Introduce the OR) (See Sec 7.1)
+
+ The interpretation of 'Payload' depends on the type of the cell.
+ PADDING: Payload is unused.
+ CREATE: Payload contains the handshake challenge.
+ CREATED: Payload contains the handshake response.
+ RELAY: Payload contains the relay header and relay body.
+ DESTROY: Payload contains a reason for closing the circuit.
+ (see 4.4)
+ Upon receiving any other value for the command field, an OR must
+ drop the cell.
+
+ The payload is padded with 0 bytes.
+
+ PADDING cells are currently used to implement connection keepalive.
+ If there is no other traffic, ORs and OPs send one another a PADDING
+ cell every few minutes.
+
+ CREATE, CREATED, and DESTROY cells are used to manage circuits;
+ see section 4 below.
+
+ RELAY cells are used to send commands and data along a circuit; see
+ section 5 below.
+
+ HELLO cells are used to introduce parameters and characteristics of
+ Tor clients and servers when connections are established.
+
+4. Circuit management
+
+4.1. CREATE and CREATED cells
+
+ Users set up circuits incrementally, one hop at a time. To create a
+ new circuit, OPs send a CREATE cell to the first node, with the
+ first half of the DH handshake; that node responds with a CREATED
+ cell with the second half of the DH handshake plus the first 20 bytes
+ of derivative key data (see section 4.2). To extend a circuit past
+ the first hop, the OP sends an EXTEND relay cell (see section 5)
+ which instructs the last node in the circuit to send a CREATE cell
+ to extend the circuit.
+
+ The payload for a CREATE cell is an 'onion skin', which consists
+ of the first step of the DH handshake data (also known as g^x).
+ This value is hybrid-encrypted (see 0.3) to Bob's public key, giving
+ an onion-skin of:
+ PK-encrypted:
+ Padding padding [PK_PAD_LEN bytes]
+ Symmetric key [KEY_LEN bytes]
+ First part of g^x [PK_ENC_LEN-PK_PAD_LEN-KEY_LEN bytes]
+ Symmetrically encrypted:
+ Second part of g^x [DH_LEN-(PK_ENC_LEN-PK_PAD_LEN-KEY_LEN)
+ bytes]
+
+ The relay payload for an EXTEND relay cell consists of:
+ Address [4 bytes]
+ Port [2 bytes]
+ Onion skin [DH_LEN+KEY_LEN+PK_PAD_LEN bytes]
+ Identity fingerprint [HASH_LEN bytes]
+
+ The port and address field denote the IPV4 address and port of the next
+ onion router in the circuit; the public key hash is the hash of the PKCS#1
+ ASN1 encoding of the next onion router's identity (signing) key. (See 0.3
+ above.) (Including this hash allows the extending OR verify that it is
+ indeed connected to the correct target OR, and prevents certain
+ man-in-the-middle attacks.)
+
+ The payload for a CREATED cell, or the relay payload for an
+ EXTENDED cell, contains:
+ DH data (g^y) [DH_LEN bytes]
+ Derivative key data (KH) [HASH_LEN bytes] <see 4.2 below>
+
+ The CircID for a CREATE cell is an arbitrarily chosen 2-byte integer,
+ selected by the node (OP or OR) that sends the CREATE cell. To prevent
+ CircID collisions, when one OR sends a CREATE cell to another, it chooses
+ from only one half of the possible values based on the ORs' public
+ identity keys: if the sending OR has a lower key, it chooses a CircID with
+ an MSB of 0; otherwise, it chooses a CircID with an MSB of 1.
+
+ Public keys are compared numerically by modulus.
+
+ As usual with DH, x and y MUST be generated randomly.
+
+4.1.1. CREATE_FAST/CREATED_FAST cells
+
+ When initializing the first hop of a circuit, the OP has already
+ established the OR's identity and negotiated a secret key using TLS.
+ Because of this, it is not always necessary for the OP to perform the
+ public key operations to create a circuit. In this case, the
+ OP MAY send a CREATE_FAST cell instead of a CREATE cell for the first
+ hop only. The OR responds with a CREATED_FAST cell, and the circuit is
+ created.
+
+ A CREATE_FAST cell contains:
+
+ Key material (X) [HASH_LEN bytes]
+
+ A CREATED_FAST cell contains:
+
+ Key material (Y) [HASH_LEN bytes]
+ Derivative key data [HASH_LEN bytes] (See 4.2 below)
+
+ The values of X and Y must be generated randomly.
+
+ [Versions of Tor before 0.1.0.6-rc did not support these cell types;
+ clients should not send CREATE_FAST cells to older Tor servers.]
+
+4.2. Setting circuit keys
+
+ Once the handshake between the OP and an OR is completed, both can
+ now calculate g^xy with ordinary DH. Before computing g^xy, both client
+ and server MUST verify that the received g^x or g^y value is not degenerate;
+ that is, it must be strictly greater than 1 and strictly less than p-1
+ where p is the DH modulus. Implementations MUST NOT complete a handshake
+ with degenerate keys. Implementations MUST NOT discard other "weak"
+ g^x values.
+
+ (Discarding degenerate keys is critical for security; if bad keys
+ are not discarded, an attacker can substitute the server's CREATED
+ cell's g^y with 0 or 1, thus creating a known g^xy and impersonating
+ the server. Discarding other keys may allow attacks to learn bits of
+ the private key.)
+
+ (The mainline Tor implementation, in the 0.1.1.x-alpha series, discarded
+ all g^x values less than 2^24, greater than p-2^24, or having more than
+ 1024-16 identical bits. This served no useful purpose, and we stopped.)
+
+ If CREATE or EXTEND is used to extend a circuit, the client and server
+ base their key material on K0=g^xy, represented as a big-endian unsigned
+ integer.
+
+ If CREATE_FAST is used, the client and server base their key material on
+ K0=X|Y.
+
+ From the base key material K0, they compute KEY_LEN*2+HASH_LEN*3 bytes of
+ derivative key data as
+ K = H(K0 | [00]) | H(K0 | [01]) | H(K0 | [02]) | ...
+
+ The first HASH_LEN bytes of K form KH; the next HASH_LEN form the forward
+ digest Df; the next HASH_LEN 41-60 form the backward digest Db; the next
+ KEY_LEN 61-76 form Kf, and the final KEY_LEN form Kb. Excess bytes from K
+ are discarded.
+
+ KH is used in the handshake response to demonstrate knowledge of the
+ computed shared key. Df is used to seed the integrity-checking hash
+ for the stream of data going from the OP to the OR, and Db seeds the
+ integrity-checking hash for the data stream from the OR to the OP. Kf
+ is used to encrypt the stream of data going from the OP to the OR, and
+ Kb is used to encrypt the stream of data going from the OR to the OP.
+
+4.3. Creating circuits
+
+ When creating a circuit through the network, the circuit creator
+ (OP) performs the following steps:
+
+ 1. Choose an onion router as an exit node (R_N), such that the onion
+ router's exit policy includes at least one pending stream that
+ needs a circuit (if there are any).
+
+ 2. Choose a chain of (N-1) onion routers
+ (R_1...R_N-1) to constitute the path, such that no router
+ appears in the path twice.
+
+ 3. If not already connected to the first router in the chain,
+ open a new connection to that router.
+
+ 4. Choose a circID not already in use on the connection with the
+ first router in the chain; send a CREATE cell along the
+ connection, to be received by the first onion router.
+
+ 5. Wait until a CREATED cell is received; finish the handshake
+ and extract the forward key Kf_1 and the backward key Kb_1.
+
+ 6. For each subsequent onion router R (R_2 through R_N), extend
+ the circuit to R.
+
+ To extend the circuit by a single onion router R_M, the OP performs
+ these steps:
+
+ 1. Create an onion skin, encrypted to R_M's public key.
+
+ 2. Send the onion skin in a relay EXTEND cell along
+ the circuit (see section 5).
+
+ 3. When a relay EXTENDED cell is received, verify KH, and
+ calculate the shared keys. The circuit is now extended.
+
+ When an onion router receives an EXTEND relay cell, it sends a CREATE
+ cell to the next onion router, with the enclosed onion skin as its
+ payload. The initiating onion router chooses some circID not yet
+ used on the connection between the two onion routers. (But see
+ section 4.1. above, concerning choosing circIDs based on
+ lexicographic order of nicknames.)
+
+ When an onion router receives a CREATE cell, if it already has a
+ circuit on the given connection with the given circID, it drops the
+ cell. Otherwise, after receiving the CREATE cell, it completes the
+ DH handshake, and replies with a CREATED cell. Upon receiving a
+ CREATED cell, an onion router packs it payload into an EXTENDED relay
+ cell (see section 5), and sends that cell up the circuit. Upon
+ receiving the EXTENDED relay cell, the OP can retrieve g^y.
+
+ (As an optimization, OR implementations may delay processing onions
+ until a break in traffic allows time to do so without harming
+ network latency too greatly.)
+
+4.4. Tearing down circuits
+
+ Circuits are torn down when an unrecoverable error occurs along
+ the circuit, or when all streams on a circuit are closed and the
+ circuit's intended lifetime is over. Circuits may be torn down
+ either completely or hop-by-hop.
+
+ To tear down a circuit completely, an OR or OP sends a DESTROY
+ cell to the adjacent nodes on that circuit, using the appropriate
+ direction's circID.
+
+ Upon receiving an outgoing DESTROY cell, an OR frees resources
+ associated with the corresponding circuit. If it's not the end of
+ the circuit, it sends a DESTROY cell for that circuit to the next OR
+ in the circuit. If the node is the end of the circuit, then it tears
+ down any associated edge connections (see section 5.1).
+
+ After a DESTROY cell has been processed, an OR ignores all data or
+ destroy cells for the corresponding circuit.
+
+ To tear down part of a circuit, the OP may send a RELAY_TRUNCATE cell
+ signaling a given OR (Stream ID zero). That OR sends a DESTROY
+ cell to the next node in the circuit, and replies to the OP with a
+ RELAY_TRUNCATED cell.
+
+ When an unrecoverable error occurs along one connection in a
+ circuit, the nodes on either side of the connection should, if they
+ are able, act as follows: the node closer to the OP should send a
+ RELAY_TRUNCATED cell towards the OP; the node farther from the OP
+ should send a DESTROY cell down the circuit.
+
+ The payload of a RELAY_TRUNCATED or DESTROY cell contains a single octet,
+ describing why the circuit is being closed or truncated. When sending a
+ TRUNCATED or DESTROY cell because of another TRUNCATED or DESTROY cell,
+ the error code should be propagated. The origin of a circuit always sets
+ this error code to 0, to avoid leaking its version.
+
+ The error codes are:
+ 0 -- NONE (No reason given.)
+ 1 -- PROTOCOL (Tor protocol violation.)
+ 2 -- INTERNAL (Internal error.)
+ 3 -- REQUESTED (A client sent a TRUNCATE command.)
+ 4 -- HIBERNATING (Not currently operating; trying to save bandwidth.)
+ 5 -- RESOURCELIMIT (Out of memory, sockets, or circuit IDs.)
+ 6 -- CONNECTFAILED (Unable to reach server.)
+ 7 -- OR_IDENTITY (Connected to server, but its OR identity was not
+ as expected.)
+ 8 -- OR_CONN_CLOSED (The OR connection that was carrying this circuit
+ died.)
+
+ [Versions of Tor prior to 0.1.0.11 didn't send reasons; implementations
+ MUST accept empty TRUNCATED and DESTROY cells.]
+
+4.5. Routing relay cells
+
+ When an OR receives a RELAY cell, it checks the cell's circID and
+ determines whether it has a corresponding circuit along that
+ connection. If not, the OR drops the RELAY cell.
+
+ Otherwise, if the OR is not at the OP edge of the circuit (that is,
+ either an 'exit node' or a non-edge node), it de/encrypts the payload
+ with the stream cipher, as follows:
+ 'Forward' relay cell (same direction as CREATE):
+ Use Kf as key; decrypt.
+ 'Back' relay cell (opposite direction from CREATE):
+ Use Kb as key; encrypt.
+ Note that in counter mode, decrypt and encrypt are the same operation.
+
+ The OR then decides whether it recognizes the relay cell, by
+ inspecting the payload as described in section 5.1 below. If the OR
+ recognizes the cell, it processes the contents of the relay cell.
+ Otherwise, it passes the decrypted relay cell along the circuit if
+ the circuit continues. If the OR at the end of the circuit
+ encounters an unrecognized relay cell, an error has occurred: the OR
+ sends a DESTROY cell to tear down the circuit.
+
+ When a relay cell arrives at an OP, the OP decrypts the payload
+ with the stream cipher as follows:
+ OP receives data cell:
+ For I=N...1,
+ Decrypt with Kb_I. If the payload is recognized (see
+ section 5.1), then stop and process the payload.
+
+ For more information, see section 5 below.
+
+5. Application connections and stream management
+
+5.1. Relay cells
+
+ Within a circuit, the OP and the exit node use the contents of
+ RELAY packets to tunnel end-to-end commands and TCP connections
+ ("Streams") across circuits. End-to-end commands can be initiated
+ by either edge; streams are initiated by the OP.
+
+ The payload of each unencrypted RELAY cell consists of:
+ Relay command [1 byte]
+ 'Recognized' [2 bytes]
+ StreamID [2 bytes]
+ Digest [4 bytes]
+ Length [2 bytes]
+ Data [CELL_LEN-14 bytes]
+
+ The relay commands are:
+ 1 -- RELAY_BEGIN [forward]
+ 2 -- RELAY_DATA [forward or backward]
+ 3 -- RELAY_END [forward or backward]
+ 4 -- RELAY_CONNECTED [backward]
+ 5 -- RELAY_SENDME [forward or backward]
+ 6 -- RELAY_EXTEND [forward]
+ 7 -- RELAY_EXTENDED [backward]
+ 8 -- RELAY_TRUNCATE [forward]
+ 9 -- RELAY_TRUNCATED [backward]
+ 10 -- RELAY_DROP [forward or backward]
+ 11 -- RELAY_RESOLVE [forward]
+ 12 -- RELAY_RESOLVED [backward]
+
+ Commands labelled as "forward" must only be sent by the originator
+ of the circuit. Commands labelled as "backward" must only be sent by
+ other nodes in the circuit back to the originator. Commands marked
+ as either can be sent either by the originator or other nodes.
+
+ The 'recognized' field in any unencrypted relay payload is always set
+ to zero; the 'digest' field is computed as the first four bytes of
+ the running digest of all the bytes that have been destined for
+ this hop of the circuit or originated from this hop of the circuit,
+ seeded from Df or Db respectively (obtained in section 4.2 above),
+ and including this RELAY cell's entire payload (taken with the digest
+ field set to zero).
+
+ When the 'recognized' field of a RELAY cell is zero, and the digest
+ is correct, the cell is considered "recognized" for the purposes of
+ decryption (see section 4.5 above).
+
+ (The digest does not include any bytes from relay cells that do
+ not start or end at this hop of the circuit. That is, it does not
+ include forwarded data. Therefore if 'recognized' is zero but the
+ digest does not match, the running digest at that node should
+ not be updated, and the cell should be forwarded on.)
+
+ All RELAY cells pertaining to the same tunneled stream have the
+ same stream ID. StreamIDs are chosen arbitrarily by the OP. RELAY
+ cells that affect the entire circuit rather than a particular
+ stream use a StreamID of zero.
+
+ The 'Length' field of a relay cell contains the number of bytes in
+ the relay payload which contain real payload data. The remainder of
+ the payload is padded with NUL bytes.
+
+ If the RELAY cell is recognized but the relay command is not
+ understood, the cell must be dropped and ignored. Its contents
+ still count with respect to the digests, though. [Before
+ 0.1.1.10, Tor closed circuits when it received an unknown relay
+ command. Perhaps this will be more forward-compatible. -RD]
+
+5.2. Opening streams and transferring data
+
+ To open a new anonymized TCP connection, the OP chooses an open
+ circuit to an exit that may be able to connect to the destination
+ address, selects an arbitrary StreamID not yet used on that circuit,
+ and constructs a RELAY_BEGIN cell with a payload encoding the address
+ and port of the destination host. The payload format is:
+
+ ADDRESS | ':' | PORT | [00]
+
+ where ADDRESS can be a DNS hostname, or an IPv4 address in
+ dotted-quad format, or an IPv6 address surrounded by square brackets;
+ and where PORT is encoded in decimal.
+
+ [What is the [00] for? -NM]
+ [It's so the payload is easy to parse out with string funcs -RD]
+
+ Upon receiving this cell, the exit node resolves the address as
+ necessary, and opens a new TCP connection to the target port. If the
+ address cannot be resolved, or a connection can't be established, the
+ exit node replies with a RELAY_END cell. (See 5.4 below.)
+ Otherwise, the exit node replies with a RELAY_CONNECTED cell, whose
+ payload is in one of the following formats:
+ The IPv4 address to which the connection was made [4 octets]
+ A number of seconds (TTL) for which the address may be cached [4 octets]
+ or
+ Four zero-valued octets [4 octets]
+ An address type (6) [1 octet]
+ The IPv6 address to which the connection was made [16 octets]
+ A number of seconds (TTL) for which the address may be cached [4 octets]
+ [XXXX Versions of Tor before 0.1.1.6 ignore and do not generate the TTL
+ field. No version of Tor currently generates the IPv6 format.
+
+ Tor servers before 0.1.2.0 set the TTL field to a fixed value. Later
+ versions set the TTL to the last value seen from a DNS server, and expire
+ their own cached entries after a fixed interval. This prevents certain
+ attacks.]
+
+ The OP waits for a RELAY_CONNECTED cell before sending any data.
+ Once a connection has been established, the OP and exit node
+ package stream data in RELAY_DATA cells, and upon receiving such
+ cells, echo their contents to the corresponding TCP stream.
+ RELAY_DATA cells sent to unrecognized streams are dropped.
+
+ Relay RELAY_DROP cells are long-range dummies; upon receiving such
+ a cell, the OR or OP must drop it.
+
+5.3. Closing streams
+
+ When an anonymized TCP connection is closed, or an edge node
+ encounters error on any stream, it sends a 'RELAY_END' cell along the
+ circuit (if possible) and closes the TCP connection immediately. If
+ an edge node receives a 'RELAY_END' cell for any stream, it closes
+ the TCP connection completely, and sends nothing more along the
+ circuit for that stream.
+
+ The payload of a RELAY_END cell begins with a single 'reason' byte to
+ describe why the stream is closing, plus optional data (depending on
+ the reason.) The values are:
+
+ 1 -- REASON_MISC (catch-all for unlisted reasons)
+ 2 -- REASON_RESOLVEFAILED (couldn't look up hostname)
+ 3 -- REASON_CONNECTREFUSED (remote host refused connection) [*]
+ 4 -- REASON_EXITPOLICY (OR refuses to connect to host or port)
+ 5 -- REASON_DESTROY (Circuit is being destroyed)
+ 6 -- REASON_DONE (Anonymized TCP connection was closed)
+ 7 -- REASON_TIMEOUT (Connection timed out, or OR timed out
+ while connecting)
+ 8 -- (unallocated) [**]
+ 9 -- REASON_HIBERNATING (OR is temporarily hibernating)
+ 10 -- REASON_INTERNAL (Internal error at the OR)
+ 11 -- REASON_RESOURCELIMIT (OR has no resources to fulfill request)
+ 12 -- REASON_CONNRESET (Connection was unexpectedly reset)
+ 13 -- REASON_TORPROTOCOL (Sent when closing connection because of
+ Tor protocol violations.)
+
+ (With REASON_EXITPOLICY, the 4-byte IPv4 address or 16-byte IPv6 address
+ forms the optional data; no other reason currently has extra data.
+ As of 0.1.1.6, the body also contains a 4-byte TTL.)
+
+ OPs and ORs MUST accept reasons not on the above list, since future
+ versions of Tor may provide more fine-grained reasons.
+
+ [*] Older versions of Tor also send this reason when connections are
+ reset.
+ [**] Due to a bug in versions of Tor through 0095, error reason 8 must
+ remain allocated until that version is obsolete.
+
+ --- [The rest of this section describes unimplemented functionality.]
+
+ Because TCP connections can be half-open, we follow an equivalent
+ to TCP's FIN/FIN-ACK/ACK protocol to close streams.
+
+ An exit connection can have a TCP stream in one of three states:
+ 'OPEN', 'DONE_PACKAGING', and 'DONE_DELIVERING'. For the purposes
+ of modeling transitions, we treat 'CLOSED' as a fourth state,
+ although connections in this state are not, in fact, tracked by the
+ onion router.
+
+ A stream begins in the 'OPEN' state. Upon receiving a 'FIN' from
+ the corresponding TCP connection, the edge node sends a 'RELAY_FIN'
+ cell along the circuit and changes its state to 'DONE_PACKAGING'.
+ Upon receiving a 'RELAY_FIN' cell, an edge node sends a 'FIN' to
+ the corresponding TCP connection (e.g., by calling
+ shutdown(SHUT_WR)) and changing its state to 'DONE_DELIVERING'.
+
+ When a stream in already in 'DONE_DELIVERING' receives a 'FIN', it
+ also sends a 'RELAY_FIN' along the circuit, and changes its state
+ to 'CLOSED'. When a stream already in 'DONE_PACKAGING' receives a
+ 'RELAY_FIN' cell, it sends a 'FIN' and changes its state to
+ 'CLOSED'.
+
+ If an edge node encounters an error on any stream, it sends a
+ 'RELAY_END' cell (if possible) and closes the stream immediately.
+
+5.4. Remote hostname lookup
+
+ To find the address associated with a hostname, the OP sends a
+ RELAY_RESOLVE cell containing the hostname to be resolved. (For a reverse
+ lookup, the OP sends a RELAY_RESOLVE cell containing an in-addr.arpa
+ address.) The OR replies with a RELAY_RESOLVED cell containing a status
+ byte, and any number of answers. Each answer is of the form:
+ Type (1 octet)
+ Length (1 octet)
+ Value (variable-width)
+ TTL (4 octets)
+ "Length" is the length of the Value field.
+ "Type" is one of:
+ 0x00 -- Hostname
+ 0x04 -- IPv4 address
+ 0x06 -- IPv6 address
+ 0xF0 -- Error, transient
+ 0xF1 -- Error, nontransient
+
+ If any answer has a type of 'Error', then no other answer may be given.
+
+ The RELAY_RESOLVE cell must use a nonzero, distinct streamID; the
+ corresponding RELAY_RESOLVED cell must use the same streamID. No stream
+ is actually created by the OR when resolving the name.
+
+6. Flow control
+
+6.1. Link throttling
+
+ Each node should do appropriate bandwidth throttling to keep its
+ user happy.
+
+ Communicants rely on TCP's default flow control to push back when they
+ stop reading.
+
+6.2. Link padding
+
+ Currently nodes are not required to do any sort of link padding or
+ dummy traffic. Because strong attacks exist even with link padding,
+ and because link padding greatly increases the bandwidth requirements
+ for running a node, we plan to leave out link padding until this
+ tradeoff is better understood.
+
+6.3. Circuit-level flow control
+
+ To control a circuit's bandwidth usage, each OR keeps track of
+ two 'windows', consisting of how many RELAY_DATA cells it is
+ allowed to package for transmission, and how many RELAY_DATA cells
+ it is willing to deliver to streams outside the network.
+ Each 'window' value is initially set to 1000 data cells
+ in each direction (cells that are not data cells do not affect
+ the window). When an OR is willing to deliver more cells, it sends a
+ RELAY_SENDME cell towards the OP, with Stream ID zero. When an OR
+ receives a RELAY_SENDME cell with stream ID zero, it increments its
+ packaging window.
+
+ Each of these cells increments the corresponding window by 100.
+
+ The OP behaves identically, except that it must track a packaging
+ window and a delivery window for every OR in the circuit.
+
+ An OR or OP sends cells to increment its delivery window when the
+ corresponding window value falls under some threshold (900).
+
+ If a packaging window reaches 0, the OR or OP stops reading from
+ TCP connections for all streams on the corresponding circuit, and
+ sends no more RELAY_DATA cells until receiving a RELAY_SENDME cell.
+[this stuff is badly worded; copy in the tor-design section -RD]
+
+6.4. Stream-level flow control
+
+ Edge nodes use RELAY_SENDME cells to implement end-to-end flow
+ control for individual connections across circuits. Similarly to
+ circuit-level flow control, edge nodes begin with a window of cells
+ (500) per stream, and increment the window by a fixed value (50)
+ upon receiving a RELAY_SENDME cell. Edge nodes initiate RELAY_SENDME
+ cells when both a) the window is <= 450, and b) there are less than
+ ten cell payloads remaining to be flushed at that edge.
+
diff --git a/doc/tor-spec.txt b/doc/tor-spec.txt
index 347397ce64..61f5fd7f15 100644
--- a/doc/tor-spec.txt
+++ b/doc/tor-spec.txt
@@ -5,25 +5,41 @@ $Id$
Roger Dingledine
Nick Mathewson
-Note: This is an attempt to specify Tor as currently implemented. Future
-versions of Tor will implement improved protocols, and compatibility is not
-guaranteed.
-
-This is not a design document; most design criteria are not examined. For
-more information on why Tor acts as it does, see tor-design.pdf.
-
-TODO: (very soon)
+Note: This document aims to specify Tor as implemented in 0.1.2.1-alpha-dev
+and later. Future versions of Tor will implement improved protocols, and
+compatibility is not guaranteed.
+
+THIS DOCUMENT IS UNSTABLE. Right now, we're revising the protocol to remove
+a few long-standing limitations. For the most stable current version of the
+protocol, see tor-spec-v0.txt; current versions of Tor are backward-compatible.
+
+This specification is not a design document; most design criteria
+are not examined. For more information on why Tor acts as it does,
+see tor-design.pdf.
+
+TODO for v1 revision:
+ - Fix onionskin handshake scheme to be more mainstream, less nutty.
+ Can we just do
+ E(HMAC(g^x), g^x) rather than just E(g^x) ?
+ No, that has the same flaws as before. We should send
+ E(g^x, C) with random C and expect g^y, HMAC_C(K=g^xy).
+ Better ask Ian; probably Stephen too.
+ - Versioned CREATE and friends
+ - Length on CREATE and friends
+ - Versioning on circuits
+
+TODO:
- REASON_CONNECTFAILED should include an IP.
- Copy prose from tor-design to make everything more readable.
-when do we rotate which keys (tls, link, etc)?
+ - Spec when we should rotate which keys (tls, link, etc)?
0. Preliminaries
0.1. Notation and encoding
PK -- a public key.
- SK -- a private key
- K -- a key for a symmetric cypher
+ SK -- a private key.
+ K -- a key for a symmetric cypher.
a|b -- concatenation of 'a' and 'b'.
@@ -37,7 +53,7 @@ when do we rotate which keys (tls, link, etc)?
0.2. Security parameters
Tor uses a stream cipher, a public-key cipher, the Diffie-Hellman
- protocol, and and a hash function.
+ protocol, and a hash function.
KEY_LEN -- the length of the stream cipher's key, in bytes.
@@ -52,6 +68,8 @@ when do we rotate which keys (tls, link, etc)?
HASH_LEN -- the length of the hash function's output, in bytes.
+ PAYLOAD_LEN -- The longest allowable cell payload, in bytes. (509)
+
CELL_LEN -- The length of a Tor cell, in bytes.
0.3. Ciphers
@@ -64,18 +82,20 @@ when do we rotate which keys (tls, link, etc)?
function. (For OAEP padding, see
ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf)
- For Diffie-Hellman, we use a generator (g) of 2. For the modulus (p), the
- 1024-bit safe prime from rfc2409, (section 6.2) whose hex representation
- is:
+ For Diffie-Hellman, we use a generator (g) of 2. For the modulus (p), we
+ use the 1024-bit safe prime from rfc2409 section 6.2 whose hex
+ representation is:
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
"49286651ECE65381FFFFFFFFFFFFFFFF"
+
As an optimization, implementations SHOULD choose DH private keys (x) of
320 bits. Implementations that do this MUST never use any DH key more
than once.
+ [May other implementations reuse their DH keys?? -RD]
For a hash function, we use SHA-1.
@@ -98,8 +118,11 @@ when do we rotate which keys (tls, link, etc)?
and let M2 = the rest of M.
Pad and encrypt K|M1 with PK. Encrypt M2 with our stream cipher,
using the key K. Concatenate these encrypted values.
- (Note that this "hybrid encryption" approach does not prevent an attacker
- from adding or removing bytes to the end of M.)
+ [XXX Note that this "hybrid encryption" approach does not prevent
+ an attacker from adding or removing bytes to the end of M. It also
+ allows attackers to modify the bytes not covered by the OAEP --
+ see Goldberg's PET2006 paper for details. We will add a MAC to this
+ scheme one day. -RD]
0.4. Other parameter values
@@ -107,7 +130,7 @@ when do we rotate which keys (tls, link, etc)?
1. System overview
- Onion Routing is a distributed overlay network designed to anonymize
+ Tor is a distributed overlay network designed to anonymize
low-latency TCP-based applications such as web browsing, secure shell,
and instant messaging. Clients choose a path through the network and
build a ``circuit'', in which each node (or ``onion router'' or ``OR'')
@@ -116,12 +139,22 @@ when do we rotate which keys (tls, link, etc)?
``cells'', which are unwrapped by a symmetric key at each node (like
the layers of an onion) and relayed downstream.
-2. Connections
+1.1. Protocol Versioning
+
+ The node-to-node TLS-based "OR connection" protocol and the multi-hop
+ "circuit" protocol are versioned quasi-independently. (Certain versions
+ of the circuit protocol may require a minimum version of the connection
+ protocol to be used.)
+
+ Version numbers are incremented for backward-incompatible protocol changes
+ only. Backward-compatible changes are generally implemented by adding
+ additional fields to existing structures; implementations MUST ignore
+ fields they do not expect.
- There are two ways to connect to an onion router (OR). The first is
- as an onion proxy (OP), which allows the OP to authenticate the OR
- without authenticating itself. The second is as another OR, which
- allows mutual authentication.
+ Parties negotiate OR connection versions as described below in sections
+ 4.1 and 4.2.
+
+2. Connections
Tor uses TLS for link encryption. All implementations MUST support
the TLS ciphersuite "TLS_EDH_RSA_WITH_DES_192_CBC3_SHA", and SHOULD
@@ -130,13 +163,25 @@ when do we rotate which keys (tls, link, etc)?
support any suite without ephemeral keys, symmetric keys of at
least KEY_LEN bits, and digests of at least HASH_LEN bits.
- An OP or OR always sends a two-certificate chain, consisting of a
+ Even though the connection protocol is identical, we think of the
+ initiator as either an onion router (OR) if it is willing to relay
+ traffic for other Tor users, or an onion proxy (OP) if it only handles
+ local requests. Onion proxies SHOULD NOT provide long-term-trackable
+ identifiers in their handshakes.
+
+ The connection initiator always sends a two-certificate chain,
+ consisting of a
certificate using a short-term connection key and a second, self-
signed certificate containing the OR's identity key. The commonName of the
first certificate is the OR's nickname, and the commonName of the second
certificate is the OR's nickname, followed by a space and the string
"<identity>".
+ Implementations running 0.2.1.0-alpha-dev and earlier used an
+ organizationName of "Tor" or "TOR". Current implementations (which
+ support the version negotiation protocol in section 4.1) MUST NOT
+ have either of these values for their organizationName.
+
All parties receiving certificates must confirm that the identity key is
as expected. (When initiating a connection, the expected identity key is
the one given in the directory; when creating a connection because of an
@@ -154,36 +199,44 @@ when do we rotate which keys (tls, link, etc)?
of TLS records MUST NOT leak information about the type or contents
of the cells.
- TLS connections are not permanent. An OP or an OR may close a
- connection to an OR if there are no circuits running over the
- connection, and an amount of time (KeepalivePeriod, defaults to 5
- minutes) has passed.
+ TLS connections are not permanent. Either side may close a connection
+ if there are no circuits running over it and an amount of time
+ (KeepalivePeriod, defaults to 5 minutes) has passed.
(As an exception, directory servers may try to stay connected to all of
- the ORs.)
+ the ORs -- though this will be phased out for the Tor 0.1.2.x release.)
3. Cell Packet format
The basic unit of communication for onion routers and onion
- proxies is a fixed-width "cell". Each cell contains the following
+ proxies is a fixed-width "cell".
+
+ On a version 0 connection, each cell contains the following
fields:
CircID [2 bytes]
Command [1 byte]
- Payload (padded with 0 bytes) [CELL_LEN-3 bytes]
- [Total size: CELL_LEN bytes]
+ Payload (padded with 0 bytes) [PAYLOAD_LEN bytes]
+
+ On a version 1 connection, each cell contains the following fields:
+
+ CircID [3 bytes]
+ Command [1 byte]
+ Payload (padded with 0 bytes) [PAYLOAD_LEN bytes]
The CircID field determines which circuit, if any, the cell is
associated with.
The 'Command' field holds one of the following values:
- 0 -- PADDING (Padding) (See Sec 6.2)
- 1 -- CREATE (Create a circuit) (See Sec 4)
- 2 -- CREATED (Acknowledge create) (See Sec 4)
- 3 -- RELAY (End-to-end data) (See Sec 5)
- 4 -- DESTROY (Stop using a circuit) (See Sec 4)
- 5 -- CREATE_FAST (Create a circuit, no PK) (See sec 4)
- 6 -- CREATED_FAST (Circtuit created, no PK) (See Sec 4)
+ 0 -- PADDING (Padding) (See Sec 7.2)
+ 1 -- CREATE (Create a circuit) (See Sec 5.1)
+ 2 -- CREATED (Acknowledge create) (See Sec 5.1)
+ 3 -- RELAY (End-to-end data) (See Sec 5.5 and 6)
+ 4 -- DESTROY (Stop using a circuit) (See Sec 5.4)
+ 5 -- CREATE_FAST (Create a circuit, no PK) (See Sec 5.1)
+ 6 -- CREATED_FAST (Circuit created, no PK) (See Sec 5.1)
+ 7 -- VERSIONS (Negotiate versions) (See Sec 4.1)
+ 8 -- NETINFO (Time and MITM-prevention) (See Sec 4.2)
The interpretation of 'Payload' depends on the type of the cell.
PADDING: Payload is unused.
@@ -191,9 +244,10 @@ when do we rotate which keys (tls, link, etc)?
CREATED: Payload contains the handshake response.
RELAY: Payload contains the relay header and relay body.
DESTROY: Payload contains a reason for closing the circuit.
- (see 4.4)
+ (see 5.4)
Upon receiving any other value for the command field, an OR must
- drop the cell.
+ drop the cell. [XXXX Versions prior to 0.1.0.?? logged a warning
+ when dropping the cell; this is bad behavior. -NM]
The payload is padded with 0 bytes.
@@ -207,15 +261,81 @@ when do we rotate which keys (tls, link, etc)?
RELAY cells are used to send commands and data along a circuit; see
section 5 below.
-4. Circuit management
+ VERSIONS cells are used to introduce parameters and characteristics of
+ Tor clients and servers when connections are established.
+
+4, Connection management
+
+ Upon establishing a TLS connection, both parties immediately begin
+ negotiating a connection protocol version and other connection parameters.
+
+4.1. VERSIONS cells
+
+ When a Tor connection is established, both parties normally send a
+ VERSIONS cell before sending any other cells. (But see below.)
+
+ NumVersions [1 byte]
+ Versions [NumVersions bytes]
+
+ "Versions" is a sequence of NumVersions link connection protocol versions,
+ each one byte long. Parties should list all of the versions which they
+ are able and willing to support. Parties can only communicate if they
+ have some connection protocol version in common.
+
+ Version 0.1.2.0-alpha and earlier don't understand VERSIONS cells,
+ and therefore don't support version negotiation. Thus, waiting until
+ the other side has sent a VERSIONS cell won't work for these servers:
+ if they send no cells back, it is impossible to tell whether they
+ have sent a VERSIONS cell that has been stalled, or whether they have
+ dropped our own VERSIONS cell as unrecognized. Thus, immediately after
+ a TLS connection has been established, the parties check whether the
+ other side has an obsolete certificate (organizationName equal to "Tor"
+ or "TOR"). If the other party presented an obsolete certificate,
+ we assume a v0 connection. Otherwise, both parties send VERSIONS
+ cells listing all their supported versions. Upon receiving the
+ other party's VERSIONS cell, the implementation begins using the
+ highest-valued version common to both cells. If the first cell from
+ the other party is _not_ a VERSIONS cell, we assume a v0 protocol.
+
+ Implementations MUST discard cells that are not the first cells sent on a
+ connection.
+
+4.2. MITM-prevention and time checking
+
+ If we negotiate a v1 connection or higher, the first cell we send SHOULD
+ be a NETINFO cell. Implementations SHOULD NOT send NETINFO cells at other
+ times.
+
+ A NETINFO cell contains:
+ Timestamp [4 bytes]
+ This OR's address [variable]
+ Other OR's address [variable]
+
+ Timestamp is the OR's current Unix time, in seconds since the epoch. If
+ an implementation receives time values from many validated ORs that
+ indicate that its clock is skewed, it SHOULD try to warn the
+ administrator.
+
+ Each address contains Type/Length/Value as used in Section 6.4. The first
+ address is the address of the interface the party sending the VERSIONS cell
+ used to connect to or accept connections from the other -- we include it
+ to block a man-in-the-middle attack on TLS that lets an attacker bounce
+ traffic through his own computers to enable timing and packet-counting
+ attacks.
-4.1. CREATE and CREATED cells
+ The second address is the one that the party sending the VERSIONS cell
+ believes the other has -- it can be used to learn what your IP address
+ is if you have no other hints.
+
+5. Circuit management
+
+5.1. CREATE and CREATED cells
Users set up circuits incrementally, one hop at a time. To create a
new circuit, OPs send a CREATE cell to the first node, with the
first half of the DH handshake; that node responds with a CREATED
cell with the second half of the DH handshake plus the first 20 bytes
- of derivative key data (see section 4.2). To extend a circuit past
+ of derivative key data (see section 5.2). To extend a circuit past
the first hop, the OP sends an EXTEND relay cell (see section 5)
which instructs the last node in the circuit to send a CREATE cell
to extend the circuit.
@@ -248,7 +368,7 @@ when do we rotate which keys (tls, link, etc)?
The payload for a CREATED cell, or the relay payload for an
EXTENDED cell, contains:
DH data (g^y) [DH_LEN bytes]
- Derivative key data (KH) [HASH_LEN bytes] <see 4.2 below>
+ Derivative key data (KH) [HASH_LEN bytes] <see 5.2 below>
The CircID for a CREATE cell is an arbitrarily chosen 2-byte integer,
selected by the node (OP or OR) that sends the CREATE cell. To prevent
@@ -261,12 +381,12 @@ when do we rotate which keys (tls, link, etc)?
As usual with DH, x and y MUST be generated randomly.
- (Older versions of Tor compared OR nicknames, and did it in a broken and
- unreliable way. To support versions of Tor earlier than 0.0.9pre6,
- implementations should notice when the other side of a connection is
- sending CREATE cells with the "wrong" MSB, and switch accordingly.)
+[
+ To implement backward-compatible version negotiation, parties MUST
+ drop CREATE cells with all-[00] onion-skins.
+]
-4.1.1. CREATE_FAST/CREATED_FAST cells
+5.1.1. CREATE_FAST/CREATED_FAST cells
When initializing the first hop of a circuit, the OP has already
established the OR's identity and negotiated a secret key using TLS.
@@ -283,25 +403,28 @@ when do we rotate which keys (tls, link, etc)?
A CREATED_FAST cell contains:
Key material (Y) [HASH_LEN bytes]
- Derivative key data [HASH_LEN bytes] (See 4.2 below)
+ Derivative key data [HASH_LEN bytes] (See 5.2 below)
The values of X and Y must be generated randomly.
[Versions of Tor before 0.1.0.6-rc did not support these cell types;
clients should not send CREATE_FAST cells to older Tor servers.]
-4.2. Setting circuit keys
+5.2. Setting circuit keys
Once the handshake between the OP and an OR is completed, both can
now calculate g^xy with ordinary DH. Before computing g^xy, both client
and server MUST verify that the received g^x or g^y value is not degenerate;
that is, it must be strictly greater than 1 and strictly less than p-1
where p is the DH modulus. Implementations MUST NOT complete a handshake
- with degenerate keys. Implementations MAY discard other "weak" g^x values.
+ with degenerate keys. Implementations MUST NOT discard other "weak"
+ g^x values.
- (Discarding degenerate keys is critical for security; if bad keys are not
- discarded, an attacker can substitute the server's CREATED cell's g^y with
- 0 or 1, thus creating a known g^xy and impersonating the server.)
+ (Discarding degenerate keys is critical for security; if bad keys
+ are not discarded, an attacker can substitute the server's CREATED
+ cell's g^y with 0 or 1, thus creating a known g^xy and impersonating
+ the server. Discarding other keys may allow attacks to learn bits of
+ the private key.)
(The mainline Tor implementation, in the 0.1.1.x-alpha series, discarded
all g^x values less than 2^24, greater than p-2^24, or having more than
@@ -330,7 +453,7 @@ when do we rotate which keys (tls, link, etc)?
is used to encrypt the stream of data going from the OP to the OR, and
Kb is used to encrypt the stream of data going from the OR to the OP.
-4.3. Creating circuits
+5.3. Creating circuits
When creating a circuit through the network, the circuit creator
(OP) performs the following steps:
@@ -371,7 +494,7 @@ when do we rotate which keys (tls, link, etc)?
cell to the next onion router, with the enclosed onion skin as its
payload. The initiating onion router chooses some circID not yet
used on the connection between the two onion routers. (But see
- section 4.1. above, concerning choosing circIDs based on
+ section 5.1. above, concerning choosing circIDs based on
lexicographic order of nicknames.)
When an onion router receives a CREATE cell, if it already has a
@@ -386,7 +509,7 @@ when do we rotate which keys (tls, link, etc)?
until a break in traffic allows time to do so without harming
network latency too greatly.)
-4.4. Tearing down circuits
+5.4. Tearing down circuits
Circuits are torn down when an unrecoverable error occurs along
the circuit, or when all streams on a circuit are closed and the
@@ -401,7 +524,7 @@ when do we rotate which keys (tls, link, etc)?
associated with the corresponding circuit. If it's not the end of
the circuit, it sends a DESTROY cell for that circuit to the next OR
in the circuit. If the node is the end of the circuit, then it tears
- down any associated edge connections (see section 5.1).
+ down any associated edge connections (see section 6.1).
After a DESTROY cell has been processed, an OR ignores all data or
destroy cells for the corresponding circuit.
@@ -429,17 +552,17 @@ when do we rotate which keys (tls, link, etc)?
2 -- INTERNAL (Internal error.)
3 -- REQUESTED (A client sent a TRUNCATE command.)
4 -- HIBERNATING (Not currently operating; trying to save bandwidth.)
- 5 -- RESOURCELIMIT (Out of memory, sockets, or circuit IDs.)
- 6 -- CONNECTFAILED (Unable to reach server.)
+ 5 -- RESOURCELIMIT (Out of memory, sockets, or circuit IDs.)
+ 6 -- CONNECTFAILED (Unable to reach server.)
7 -- OR_IDENTITY (Connected to server, but its OR identity was not
as expected.)
8 -- OR_CONN_CLOSED (The OR connection that was carrying this circuit
died.)
- [Versions of Tor prior to 0.1.0.11 didn't sent versions; implementations
+ [Versions of Tor prior to 0.1.0.11 didn't send reasons; implementations
MUST accept empty TRUNCATED and DESTROY cells.]
-4.5. Routing relay cells
+5.5. Routing relay cells
When an OR receives a RELAY cell, it checks the cell's circID and
determines whether it has a corresponding circuit along that
@@ -455,7 +578,7 @@ when do we rotate which keys (tls, link, etc)?
Note that in counter mode, decrypt and encrypt are the same operation.
The OR then decides whether it recognizes the relay cell, by
- inspecting the payload as described in section 5.1 below. If the OR
+ inspecting the payload as described in section 6.1 below. If the OR
recognizes the cell, it processes the contents of the relay cell.
Otherwise, it passes the decrypted relay cell along the circuit if
the circuit continues. If the OR at the end of the circuit
@@ -467,13 +590,13 @@ when do we rotate which keys (tls, link, etc)?
OP receives data cell:
For I=N...1,
Decrypt with Kb_I. If the payload is recognized (see
- section 5.1), then stop and process the payload.
+ section 6..1), then stop and process the payload.
- For more information, see section 5 below.
+ For more information, see section 6 below.
-5. Application connections and stream management
+6. Application connections and stream management
-5.1. Relay cells
+6.1. Relay cells
Within a circuit, the OP and the exit node use the contents of
RELAY packets to tunnel end-to-end commands and TCP connections
@@ -511,13 +634,13 @@ when do we rotate which keys (tls, link, etc)?
to zero; the 'digest' field is computed as the first four bytes of
the running digest of all the bytes that have been destined for
this hop of the circuit or originated from this hop of the circuit,
- seeded from Df or Db respectively (obtained in section 4.2 above),
+ seeded from Df or Db respectively (obtained in section 5.2 above),
and including this RELAY cell's entire payload (taken with the digest
field set to zero).
When the 'recognized' field of a RELAY cell is zero, and the digest
is correct, the cell is considered "recognized" for the purposes of
- decryption (see section 4.5 above).
+ decryption (see section 5.5 above).
(The digest does not include any bytes from relay cells that do
not start or end at this hop of the circuit. That is, it does not
@@ -540,7 +663,7 @@ when do we rotate which keys (tls, link, etc)?
0.1.1.10, Tor closed circuits when it received an unknown relay
command. Perhaps this will be more forward-compatible. -RD]
-5.2. Opening streams and transferring data
+6.2. Opening streams and transferring data
To open a new anonymized TCP connection, the OP chooses an open
circuit to an exit that may be able to connect to the destination
@@ -560,7 +683,7 @@ when do we rotate which keys (tls, link, etc)?
Upon receiving this cell, the exit node resolves the address as
necessary, and opens a new TCP connection to the target port. If the
address cannot be resolved, or a connection can't be established, the
- exit node replies with a RELAY_END cell. (See 5.4 below.)
+ exit node replies with a RELAY_END cell. (See 6.4 below.)
Otherwise, the exit node replies with a RELAY_CONNECTED cell, whose
payload is in one of the following formats:
The IPv4 address to which the connection was made [4 octets]
@@ -571,7 +694,12 @@ when do we rotate which keys (tls, link, etc)?
The IPv6 address to which the connection was made [16 octets]
A number of seconds (TTL) for which the address may be cached [4 octets]
[XXXX Versions of Tor before 0.1.1.6 ignore and do not generate the TTL
- field. No version of Tor currently generates the IPv6 format.]
+ field. No version of Tor currently generates the IPv6 format.
+
+ Tor servers before 0.1.2.0 set the TTL field to a fixed value. Later
+ versions set the TTL to the last value seen from a DNS server, and expire
+ their own cached entries after a fixed interval. This prevents certain
+ attacks.]
The OP waits for a RELAY_CONNECTED cell before sending any data.
Once a connection has been established, the OP and exit node
@@ -582,7 +710,7 @@ when do we rotate which keys (tls, link, etc)?
Relay RELAY_DROP cells are long-range dummies; upon receiving such
a cell, the OR or OP must drop it.
-5.3. Closing streams
+6.3. Closing streams
When an anonymized TCP connection is closed, or an edge node
encounters error on any stream, it sends a 'RELAY_END' cell along the
@@ -650,7 +778,7 @@ when do we rotate which keys (tls, link, etc)?
If an edge node encounters an error on any stream, it sends a
'RELAY_END' cell (if possible) and closes the stream immediately.
-5.4. Remote hostname lookup
+6.4. Remote hostname lookup
To find the address associated with a hostname, the OP sends a
RELAY_RESOLVE cell containing the hostname to be resolved. (For a reverse
@@ -675,9 +803,9 @@ when do we rotate which keys (tls, link, etc)?
corresponding RELAY_RESOLVED cell must use the same streamID. No stream
is actually created by the OR when resolving the name.
-6. Flow control
+7. Flow control
-6.1. Link throttling
+7.1. Link throttling
Each node should do appropriate bandwidth throttling to keep its
user happy.
@@ -685,7 +813,11 @@ when do we rotate which keys (tls, link, etc)?
Communicants rely on TCP's default flow control to push back when they
stop reading.
-6.2. Link padding
+7.2. Link padding
+
+ Link padding can be created by sending PADDING cells along the
+ connection; relay cells of type "DROP" can be used for long-range
+ padding.
Currently nodes are not required to do any sort of link padding or
dummy traffic. Because strong attacks exist even with link padding,
@@ -693,7 +825,7 @@ when do we rotate which keys (tls, link, etc)?
for running a node, we plan to leave out link padding until this
tradeoff is better understood.
-6.3. Circuit-level flow control
+7.3. Circuit-level flow control
To control a circuit's bandwidth usage, each OR keeps track of
two 'windows', consisting of how many RELAY_DATA cells it is
@@ -719,7 +851,7 @@ when do we rotate which keys (tls, link, etc)?
sends no more RELAY_DATA cells until receiving a RELAY_SENDME cell.
[this stuff is badly worded; copy in the tor-design section -RD]
-6.4. Stream-level flow control
+7.4. Stream-level flow control
Edge nodes use RELAY_SENDME cells to implement end-to-end flow
control for individual connections across circuits. Similarly to
@@ -729,290 +861,6 @@ when do we rotate which keys (tls, link, etc)?
cells when both a) the window is <= 450, and b) there are less than
ten cell payloads remaining to be flushed at that edge.
-7. Directories and routers
-
-7.1. Extensible information format
-
-Router descriptors and directories both obey the following lightweight
-extensible information format.
-
-The highest level object is a Document, which consists of one or more Items.
-Every Item begins with a KeywordLine, followed by one or more Objects. A
-KeywordLine begins with a Keyword, optionally followed by whitespace and more
-non-newline characters, and ends with a newline. A Keyword is a sequence of
-one or more characters in the set [A-Za-z0-9-]. An Object is a block of
-encoded data in pseudo-Open-PGP-style armor. (cf. RFC 2440)
-
-More formally:
-
- Document ::= (Item | NL)+
- Item ::= KeywordLine Object*
- KeywordLine ::= Keyword NL | Keyword WS ArgumentsChar+ NL
- Keyword = KeywordChar+
- KeywordChar ::= 'A' ... 'Z' | 'a' ... 'z' | '0' ... '9' | '-'
- ArgumentChar ::= any printing ASCII character except NL.
- WS = (SP | TAB)+
- Object ::= BeginLine Base-64-encoded-data EndLine
- BeginLine ::= "-----BEGIN " Keyword "-----" NL
- EndLine ::= "-----END " Keyword "-----" NL
-
- The BeginLine and EndLine of an Object must use the same keyword.
-
-When interpreting a Document, software MUST reject any document containing a
-KeywordLine that starts with a keyword it doesn't recognize.
-
-The "opt" keyword is reserved for non-critical future extensions. All
-implementations MUST ignore any item of the form "opt keyword ....." when
-they would not recognize "keyword ....."; and MUST treat "opt keyword ....."
-as synonymous with "keyword ......" when keyword is recognized.
-
-7.2. Router descriptor format.
-
-Every router descriptor MUST start with a "router" Item; MUST end with a
-"router-signature" Item and an extra NL; and MUST contain exactly one
-instance of each of the following Items: "published" "onion-key" "link-key"
-"signing-key" "bandwidth". Additionally, a router descriptor MAY contain any
-number of "accept", "reject", "fingerprint", "uptime", and "opt" Items.
-Other than "router" and "router-signature", the items may appear in any
-order.
-
-The items' formats are as follows:
- "router" nickname address ORPort SocksPort DirPort
-
- Indicates the beginning of a router descriptor. "address"
- must be an IPv4 address in dotted-quad format. The last
- three numbers indicate the TCP ports at which this OR exposes
- functionality. ORPort is a port at which this OR accepts TLS
- connections for the main OR protocol; SocksPort is deprecated and
- should always be 0; and DirPort is the port at which this OR accepts
- directory-related HTTP connections. If any port is not supported,
- the value 0 is given instead of a port number.
-
- "bandwidth" bandwidth-avg bandwidth-burst bandwidth-observed
-
- Estimated bandwidth for this router, in bytes per second. The
- "average" bandwidth is the volume per second that the OR is willing
- to sustain over long periods; the "burst" bandwidth is the volume
- that the OR is willing to sustain in very short intervals. The
- "observed" value is an estimate of the capacity this server can
- handle. The server remembers the max bandwidth sustained output
- over any ten second period in the past day, and another sustained
- input. The "observed" value is the lesser of these two numbers.
-
- "platform" string
-
- A human-readable string describing the system on which this OR is
- running. This MAY include the operating system, and SHOULD include
- the name and version of the software implementing the Tor protocol.
-
- "published" YYYY-MM-DD HH:MM:SS
-
- The time, in GMT, when this descriptor was generated.
-
- "fingerprint"
-
- A fingerprint (a HASH_LEN-byte of asn1 encoded public key, encoded
- in hex, with a single space after every 4 characters) for this router's
- identity key.
-
- [We didn't start parsing this line until Tor 0.1.0.6-rc; it should
- be marked with "opt" until earlier versions of Tor are obsolete.]
-
- "hibernating" 0|1
-
- If the value is 1, then the Tor server was hibernating when the
- descriptor was published, and shouldn't be used to build circuits.
-
- [We didn't start parsing this line until Tor 0.1.0.6-rc; it should
- be marked with "opt" until earlier versions of Tor are obsolete.]
-
- "uptime"
-
- The number of seconds that this OR process has been running.
-
- "onion-key" NL a public key in PEM format
-
- This key is used to encrypt EXTEND cells for this OR. The key MUST
- be accepted for at least XXXX hours after any new key is published in
- a subsequent descriptor.
-
- "signing-key" NL a public key in PEM format
-
- The OR's long-term identity key.
-
- "accept" exitpattern
- "reject" exitpattern
-
- These lines, in order, describe the rules that an OR follows when
- deciding whether to allow a new stream to a given address. The
- 'exitpattern' syntax is described below.
-
- "router-signature" NL Signature NL
-
- The "SIGNATURE" object contains a signature of the PKCS1-padded
- hash of the entire router descriptor, taken from the beginning of the
- "router" line, through the newline after the "router-signature" line.
- The router descriptor is invalid unless the signature is performed
- with the router's identity key.
-
- "contact" info NL
-
- Describes a way to contact the server's administrator, preferably
- including an email address and a PGP key fingerprint.
-
- "family" names NL
-
- 'Names' is a whitespace-separated list of server nicknames. If two ORs
- list one another in their "family" entries, then OPs should treat them
- as a single OR for the purpose of path selection.
-
- For example, if node A's descriptor contains "family B", and node B's
- descriptor contains "family A", then node A and node B should never
- be used on the same circuit.
-
- "read-history" YYYY-MM-DD HH:MM:SS (NSEC s) NUM,NUM,NUM,NUM,NUM... NL
- "write-history" YYYY-MM-DD HH:MM:SS (NSEC s) NUM,NUM,NUM,NUM,NUM... NL
-
- Declare how much bandwidth the OR has used recently. Usage is divided
- into intervals of NSEC seconds. The YYYY-MM-DD HH:MM:SS field defines
- the end of the most recent interval. The numbers are the number of
- bytes used in the most recent intervals, ordered from oldest to newest.
-
- [We didn't start parsing these lines until Tor 0.1.0.6-rc; they should
- be marked with "opt" until earlier versions of Tor are obsolete.]
-
-nickname ::= between 1 and 19 alphanumeric characters, case-insensitive.
-
-exitpattern ::= addrspec ":" portspec
-portspec ::= "*" | port | port "-" port
-port ::= an integer between 1 and 65535, inclusive.
-addrspec ::= "*" | ip4spec | ip6spec
-ipv4spec ::= ip4 | ip4 "/" num_ip4_bits | ip4 "/" ip4mask
-ip4 ::= an IPv4 address in dotted-quad format
-ip4mask ::= an IPv4 mask in dotted-quad format
-num_ip4_bits ::= an integer between 0 and 32
-ip6spec ::= ip6 | ip6 "/" num_ip6_bits
-ip6 ::= an IPv6 address, surrounded by square brackets.
-num_ip6_bits ::= an integer between 0 and 128
-
-Ports are required; if they are not included in the router
-line, they must appear in the "ports" lines.
-
-7.3. Directory format
-
-A Directory begins with a "signed-directory" item, followed by one each of
-the following, in any order: "recommended-software", "published",
-"router-status", "dir-signing-key". It may include any number of "opt"
-items. After these items, a directory includes any number of router
-descriptors, and a single "directory-signature" item.
-
- "signed-directory"
-
- Indicates the start of a directory.
-
- "published" YYYY-MM-DD HH:MM:SS
-
- The time at which this directory was generated and signed, in GMT.
-
- "dir-signing-key"
-
- The key used to sign this directory; see "signing-key" for format.
-
- "recommended-software" comma-separated-version-list
-
- A list of which versions of which implementations are currently
- believed to be secure and compatible with the network.
-
- "running-routers" whitespace-separated-list
-
- A description of which routers are currently believed to be up or
- down. Every entry consists of an optional "!", followed by either an
- OR's nickname, or "$" followed by a hexadecimal encoding of the hash
- of an OR's identity key. If the "!" is included, the router is
- believed not to be running; otherwise, it is believed to be running.
- If a router's nickname is given, exactly one router of that nickname
- will appear in the directory, and that router is "approved" by the
- directory server. If a hashed identity key is given, that OR is not
- "approved". [XXXX The 'running-routers' line is only provided for
- backward compatibility. New code should parse 'router-status'
- instead.]
-
- "router-status" whitespace-separated-list
-
- A description of which routers are currently believed to be up or
- down, and which are verified or unverified. Contains one entry for
- every router that the directory server knows. Each entry is of the
- format:
-
- !name=$digest [Verified router, currently not live.]
- name=$digest [Verified router, currently live.]
- !$digest [Unverified router, currently not live.]
- or $digest [Unverified router, currently live.]
-
- (where 'name' is the router's nickname and 'digest' is a hexadecimal
- encoding of the hash of the routers' identity key).
-
- When parsing this line, clients should only mark a router as
- 'verified' if its nickname AND digest match the one provided.
-
- "directory-signature" nickname-of-dirserver NL Signature
-
-The signature is computed by computing the digest of the
-directory, from the characters "signed-directory", through the newline
-after "directory-signature". This digest is then padded with PKCS.1,
-and signed with the directory server's signing key.
-
-If software encounters an unrecognized keyword in a single router descriptor,
-it MUST reject only that router descriptor, and continue using the
-others. Because this mechanism is used to add 'critical' extensions to
-future versions of the router descriptor format, implementation should treat
-it as a normal occurrence and not, for example, report it to the user as an
-error. [Versions of Tor prior to 0.1.1 did this.]
-
-If software encounters an unrecognized keyword in the directory header,
-it SHOULD reject the entire directory.
-
-7.4. Network-status descriptor
-
-A "network-status" (a.k.a "running-routers") document is a truncated
-directory that contains only the current status of a list of nodes, not
-their actual descriptors. It contains exactly one of each of the following
-entries.
-
- "network-status"
-
- Must appear first.
-
- "published" YYYY-MM-DD HH:MM:SS
-
- (see 7.3 above)
-
- "router-status" list
-
- (see 7.3 above)
-
- "directory-signature" NL signature
-
- (see 7.3 above)
-
-7.5. Behavior of a directory server
-
-lists nodes that are connected currently
-speaks HTTP on a socket, spits out directory on request
-
-Directory servers listen on a certain port (the DirPort), and speak a
-limited version of HTTP 1.0. Clients send either GET or POST commands.
-The basic interactions are:
- "%s %s HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s\r\n\r\n",
- command, url, content-length, host.
- Get "/tor/" to fetch a full directory.
- Get "/tor/dir.z" to fetch a compressed full directory.
- Get "/tor/running-routers" to fetch a network-status descriptor.
- Post "/tor/" to post a server descriptor, with the body of the
- request containing the descriptor.
-
- "host" is used to specify the address:port of the dirserver, so
- the request can survive going through HTTP proxies.
A.1. Differences between spec and implementation
@@ -1033,7 +881,7 @@ B.1. ... but which will require backward-incompatible change
- Drop backward compatibility.
- We should use a 128-bit subgroup of our DH prime.
- Handshake should use HMAC.
- - Multiple cell lengths
+ - Multiple cell lengths.
- Ability to split circuits across paths (If this is useful.)
- SENDME windows should be dynamic.
diff --git a/doc/tor-win32-mingw-creation.txt b/doc/tor-win32-mingw-creation.txt
new file mode 100644
index 0000000000..a17d37a806
--- /dev/null
+++ b/doc/tor-win32-mingw-creation.txt
@@ -0,0 +1,120 @@
+## Instructions for building Tor with MinGW (http://www.mingw.org/)
+##
+
+Stage One: Download and Install MinGW.
+---------------------------------------
+
+Download mingw:
+http://prdownloads.sf.net/mingw/MinGW-5.0.3.exe?download
+
+Download msys:
+http://prdownloads.sf.net/mingw/MSYS-1.0.10.exe?download
+
+Download the mingw developer tool kit:
+http://prdownloads.sf.net/mingw/msysDTK-1.0.1.exe?download
+
+Download the mingw win32api:
+http://prdownloads.sf.net/mingw/w32api-3.6.tar.gz?download
+
+Install mingw, msys and mingw-dtk.
+
+Create a directory called "tor-mingw".
+
+Stage Two: Download, extract, compile openssl
+----------------------------------------------
+
+Download openssl:
+http://www.openssl.org/source/openssl-0.9.8c.tar.gz
+
+Extract openssl:
+Copy the openssl tarball into the "tor-mingw" directory.
+Type "cd tor-mingw/"
+Type "tar zxf openssl-0.9.8c.tar.gz"
+
+Make openssl libraries:
+Type "cd tor-mingw/openssl-0.9.8c/"
+Type "./Configure mingw"
+Edit Makefile and remove the "test:" and "tests:" sections.
+Type "rm -rf ./test"
+Type "cd crypto/"
+Type "find ./ -name "*.h" -exec cp {} ../include/openssl/ \;"
+Type "cd ../ssl/"
+Type "find ./ -name "*.h" -exec cp {} ../include/openssl/ \;
+Type "cd .."
+Type "cp *.h include/openssl/"
+# The next steps can take up to 30 minutes to complete.
+Type "make"
+Type "make install"
+
+Alternatively:
+Download the pre-compiled openssl for win32.
+Install and proceed.
+
+
+Stage Three: Download, extract, compile zlib
+---------------------------------------------
+
+Download zlib source:
+http://www.zlib.net/zlib-1.2.3.tar.gz
+
+Extract zlib:
+Copy the zlib tarball into the "tor-mingw" directory
+Type "cd tor-mingw/"
+Type "tar zxf zlib-1.2.3.tar.gz"
+
+CHOICE:
+
+Make zlib.a:
+Type "cd tor-mingw/zlib-1.2.3/"
+Type "./configure"
+Type "make"
+Type "make install"
+
+OR
+
+Make zlib1.dll:
+Type "cd tor-mingw/zlib-1.2.3"/
+Type "./configure"
+Type "make -f win32/Makefile.gcc"
+
+Done.
+
+
+Stage Four: Download, extract, and patch libevent-1.1b.
+------------------------------------------------------
+
+Download libevent-1.1b:
+http://www.monkey.org/~provos/libevent/
+
+Copy the libevent tarball into the "tor-mingw" directory.
+Type "cd tor-mingw"
+
+Extract libevent:
+Type "tar zxf libevent-1.1b.tar.gz"
+
+Download the libevent-1.1b-mingw.diff from
+http://cvsview.seul.org/tor/something/path.
+Copy the libevent-1.1b diff into the libevent-1.1b directory.
+Type "patch -p0 < libevent-1.1b-mingw.diff"
+
+Type "aclocal && autoheader && automake && autoconf".
+There may be WARNING messages. There will be no output if all runs successfuly.
+
+Type "./configure --enable-static --disable-shared"
+Type "make"
+Type "make install"
+
+Stage Five: Build Tor
+----------------------
+
+Download the current Tor alpha release from http://tor.eff.org/download.html.
+Copy the Tor tarball into the "tor-mingw" directory.
+Extract Tor:
+Type "tar zxf latest-tor-alpha.tar.gz"
+
+cd tor-<version>
+Type "./configure --enable-static --disable-shared"
+Type "make"
+
+You now have a tor.exe in src/or/. This is Tor.
+You now have a tor_resolve.exe in src/tools/.
diff --git a/doc/tor.1.in b/doc/tor.1.in
index 656a259249..effd59b7ef 100644
--- a/doc/tor.1.in
+++ b/doc/tor.1.in
@@ -198,6 +198,12 @@ of Tor's outgoing connections to use a single one.
On startup, write our PID to FILE. On clean shutdown, remove FILE.
.LP
.TP
+\fBProtocolWarnings \fR\fB0\fR|\fB1\fR\fP
+If 1, Tor will log with severity 'warn' various cases of other parties
+not following the Tor specification. Otherwise, they are logged with
+severity 'info'. (Default: 0)
+.LP
+.TP
\fBRunAsDaemon \fR\fB0\fR|\fB1\fR\fP
If 1, Tor forks and daemonizes to the background. This option has
no effect on Windows; instead you should use the --service command-line
@@ -215,7 +221,7 @@ about what sites a user might have visited. (Default: 1)
On startup, setuid to this user.
.LP
.TP
-\fBHardwareAccel \fR\fI0|1\fP
+\fBHardwareAccel \fR\fB0\fR|\fB1\fP
If non-zero, try to use crypto hardware acceleration when
available. This is untested and probably buggy. (Default: 0)
@@ -225,10 +231,11 @@ The following options are useful only for clients (that is, if \fBSocksPort\fP i
.LP
.TP
\fBAllowInvalidNodes\fR \fBentry\fR|\fBexit\fR|\fBmiddle\fR|\fBintroduction\fR|\fBrendezvous\fR|...\fP
-Allow routers that the dirserver operators consider invalid (not
-trustworthy or otherwise not working right) in only these positions in
-your circuits.
-The default is "middle,rendezvous", and other choices are not advised.
+If some Tor servers are obviously not working right, the directory
+authorities can manually mark them as invalid, meaning that it's not
+recommended you use them for entry or exit positions in your circuits. You
+can opt to use them in some circuit positions, though. The default is
+"middle,rendezvous", and other choices are not advised.
.LP
.TP
\fBCircuitBuildTimeout \fR\fINUM\fP
@@ -310,7 +317,7 @@ firewall allows connections to everything inside net 99, rejects port
\fBReachableDirAddresses \fR\fIADDR\fP[\fB/\fP\fIMASK\fP][:\fIPORT\fP]...\fP
Like \fBReachableAddresses\fP, a list of addresses and ports. Tor will obey
these restrictions when fetching directory information, using standard HTTP
-GET requests. If not set explicitly then the value of \fBfBReachableAddresses\fP
+GET requests. If not set explicitly then the value of \fBReachableAddresses\fP
is used. If \fBHttpProxy\fR is set then these connections will go through that
proxy.
.LP
@@ -318,7 +325,7 @@ proxy.
\fBReachableORAddresses \fR\fIADDR\fP[\fB/\fP\fIMASK\fP][:\fIPORT\fP]...\fP
Like \fBReachableAddresses\fP, a list of addresses and ports. Tor will obey
these restrictions when connecting to Onion Routers, using TLS/SSL. If not set
-explicitly then the value of \fBfBReachableAddresses\fP is used. If
+explicitly then the value of \fBReachableAddresses\fP is used. If
\fBHttpsProxy\fR is set then these connections will go through that proxy.
The separation between \fBReachableORAddresses\fP and
@@ -333,14 +340,15 @@ port 80.
A list of ports for services that tend to have long-running connections
(e.g. chat and interactive shells). Circuits for streams that use these
ports will contain only high-uptime nodes, to reduce the chance that a
-node will go down before the stream is finished. (Default: 21, 22, 706, 1863, 5050,
-5190, 5222, 5223, 6667, 8300, 8888)
+node will go down before the stream is finished.
+(Default: 21, 22, 706, 1863, 5050, 5190, 5222, 5223, 6667, 8300)
.LP
.TP
\fBMapAddress\fR \fIaddress\fR \fInewaddress\fR
-When a request for address arrives to Tor, it will rewrite it to newaddress before
-processing it. For example, if you always want connections to www.indymedia.org to
-exit via \fItorserver\fR (where \fItorserver\fR is the nickname of the server),
+When a request for address arrives to Tor, it will rewrite it to
+newaddress before processing it. For example, if you always want
+connections to www.indymedia.org to exit via \fItorserver\fR (where
+\fItorserver\fR is the nickname of the server),
use "MapAddress www.indymedia.org www.indymedia.org.torserver.exit".
.LP
.TP
@@ -349,8 +357,9 @@ Every NUM seconds consider whether to build a new circuit. (Default: 30 seconds)
.LP
.TP
\fBMaxCircuitDirtiness \fR\fINUM\fP
-Feel free to reuse a circuit that was first used at most NUM seconds
-ago, but never attach a new stream to a circuit that is too old. (Default: 10 minutes)
+Feel free to reuse a circuit that was first used at most NUM seconds ago,
+but never attach a new stream to a circuit that is too old.
+(Default: 10 minutes)
.LP
.TP
\fBNodeFamily \fR\fInickname\fR,\fInickname\fR,\fI...\fP
@@ -361,7 +370,7 @@ NodeFamily is only needed when a server doesn't list the family itself
.LP
.TP
.\" \fBPathlenCoinWeight \fR\fI0.0-1.0\fP
-.\" Paths are 3 hops plus a geometric distribution centered around this coinweight.
+.\" Paths are 3 hops plus a geometric distribution centered around this coinweight.
.\" Must be >=0.0 and <1.0. (Default: 0.3) NOT USED CURRENTLY
.\" .TP
\fBRendNodes \fR\fInickname\fR,\fInickname\fR,\fI...\fP
@@ -396,6 +405,11 @@ Let a socks connection wait NUM seconds unattached before we fail it.
(Default: 2 minutes.)
.LP
.TP
+\fBTestVia \fR\fInickname\fR,\fInickname\fR,\fI...\fP
+A list of nodes to prefer for your middle hop when building testing
+circuits. This option is mainly for debugging reachability problems.
+.LP
+.TP
\fBTrackHostExits \fR\fIhost\fR,\fI.domain\fR,\fI...\fR\fP
For each value in the comma separated list, Tor will track recent connections
to hosts that match this value and attempt to
@@ -415,7 +429,7 @@ between host and exit server after NUM seconds. The default
is 1800 seconds (30 minutes).
.LP
.TP
-\fBUseEntryGuards \fR\fI0|1\fP
+\fBUseEntryGuards \fR\fB0\fR|\fB1\fR\fP
If this option is set to 1, we pick a few long-term entry servers, and
try to stick with them. This is desirable because
constantly changing servers increases the odds that an adversary who owns
@@ -429,7 +443,7 @@ as long-term entries for our circuits.
(Defaults to 3.)
.LP
.TP
-\fBSafeSocks \fR\fI0|1\fP
+\fBSafeSocks \fR\fB0\fR|\fB1\fR\fP
When this option is enabled, Tor will reject application connections that
use unsafe variants of the socks protocol -- ones that only provide an
IP address, meaning the application is doing a DNS resolve first.
@@ -470,7 +484,9 @@ list all connected servers as running.
.LP
.TP
\fBContactInfo \fR\fIemail_address\fP
-Administrative contact information for server.
+Administrative contact information for server. This line might get
+picked up by spam harvesters, so you may want to obscure the fact
+that it's an email address.
.LP
.TP
\fBExitPolicy \fR\fIpolicy\fR,\fIpolicy\fR,\fI...\fP
@@ -614,6 +630,32 @@ as day 1 and Sunday as day 7. If \fBday\fR is given, each accounting
period runs from the time \fIHH:MM\fR each day to the same time on the
next day. All times are local, and given in 24-hour time. (Defaults to
"month 1 0:00".)
+.LP
+.TP
+\fBServerDNSResolvConfFile \fR\fIfilename\fP
+Overrides the default DNS configuration with the configuration in
+\fIfilename\fP. The file format is the same as the standard Unix
+"\fBresolv.conf\fP" file (7). This option only effects name lookup for
+addresses requested by clients; and only takes effect if Tor was built with
+eventdns support. (Defaults to use the system DNS configuration.)
+.LP
+.TP
+\fBServerDNSSearchDomains \fR\fB0\fR|\fB1\fR\fP
+If set to \fB1\fP, then we will search for addresses in the local search
+domain. For example, if this system is configured to believe it is in
+"example.com", and a client tries to connect to "www", the client will be
+connected to "www.example.com".
+This option only effects name lookup for addresses requested by clients.
+(Defaults to "0".)
+.LP
+.TP
+\fBServerDNSDetectHijacking \fR\fB0\fR|\fB1\fR\fP
+When this option is set to 1, we will test periodically to determine whether
+our local nameservers have been configured to hijack failing DNS requests
+(usually to an advertising site). If they are, we will attempt to correct
+this. This option only effects name lookup for addresses requested by
+clients; and only takes effect if Tor was built with eventdns support.
+(Defaults to "1".)
.SH DIRECTORY SERVER OPTIONS
.PP
@@ -667,7 +709,8 @@ to this port rather than the one specified in DirPort. (Default: 0.0.0.0)
.LP
.TP
\fBDirPolicy \fR\fIpolicy\fR,\fIpolicy\fR,\fI...\fP
-Set an entrance policy for this server, to limit who can connect to the directory ports.
+Set an entrance policy for this server, to limit who can connect to the
+directory ports.
The policies have the same form as exit policies above.
.LP
.TP
diff --git a/src/common/compat.c b/src/common/compat.c
index 3a05c32d45..c98748bcd2 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -24,8 +24,9 @@ const char compat_c_id[] =
#ifdef MS_WINDOWS
#include <process.h>
-
+#include <windows.h>
#endif
+
#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif
@@ -87,6 +88,13 @@ const char compat_c_id[] =
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef USE_BSOCKETS
+#include <bsocket.h>
+#endif
#include "log.h"
#include "util.h"
@@ -104,6 +112,135 @@ const char compat_c_id[] =
#define INADDR_NONE ((unsigned long) -1)
#endif
+#ifdef HAVE_SYS_MMAN_H
+tor_mmap_t *
+tor_mmap_file(const char *filename)
+{
+ int fd; /* router file */
+ char *string;
+ int page_size;
+ tor_mmap_t *res;
+ size_t size;
+
+ tor_assert(filename);
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd<0) {
+ log_warn(LD_FS,"Could not open \"%s\" for mmap().",filename);
+ return NULL;
+ }
+
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ /* ensure page alignment */
+ page_size = getpagesize();
+ size += (size%page_size) ? page_size-(size%page_size) : 0;
+
+ string = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (string == MAP_FAILED) {
+ close(fd);
+ log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
+ strerror(errno));
+ return NULL;
+ }
+
+ close(fd);
+
+ res = tor_malloc_zero(sizeof(tor_mmap_t));
+ res->data = string;
+ res->size = size;
+
+ return res;
+}
+void
+tor_munmap_file(tor_mmap_t *handle)
+{
+ munmap((char*)handle->data, handle->size);
+ tor_free(handle);
+}
+#elif defined(MS_WINDOWS)
+typedef struct win_mmap_t {
+ tor_mmap_t base;
+ HANDLE file_handle;
+ HANDLE mmap_handle;
+} tor_mmap_impl_t;
+tor_mmap_t *
+tor_mmap_file(const char *filename)
+{
+ struct win_mmap_t *res = tor_malloc_zero(sizeof(struct win_mmap_t));
+ res->mmap_handle = res->file_handle = INVALID_HANDLE_VALUE;
+
+ res->file_handle = CreateFile(filename,
+ GENERIC_READ,
+ 0, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+
+ if (res->file_handle == INVALID_HANDLE_VALUE)
+ goto err;
+
+ res->base.size = GetFileSize(res->file_handle, NULL);
+
+ res->mmap_handle = CreateFileMapping(res->file_handle,
+ NULL,
+ PAGE_READONLY,
+ (res->base.size >> 32),
+ (res->base.size & 0xfffffffful),
+ NULL);
+ if (res->mmap_handle != INVALID_HANDLE_VALUE)
+ goto err;
+ res->base.data = (char*) MapViewOfFile(res->mmap_handle,
+ FILE_MAP_READ,
+ 0, 0, 0);
+ if (!res->base.data)
+ goto err;
+
+ return &(res->base);
+ err:
+ tor_munmap_file(&res->base);
+ return NULL;
+}
+void
+tor_munmap_file(tor_mmap_t *handle)
+{
+ struct win_mmap_t *h = (struct win_mmap_t*)
+ (((char*)handle) - STRUCT_OFFSET(struct win_mmap_t, base));
+ if (handle->data)
+
+ /*this is an ugly cast, but without it, "data" in struct tor_mmap_t would
+ have to be redefined as const*/
+ UnmapViewOfFile( (LPVOID) handle->data);
+
+ if (h->mmap_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(h->mmap_handle);
+ if (h->file_handle != INVALID_HANDLE_VALUE)
+ CloseHandle(h->file_handle);
+ tor_free(h);
+}
+#else
+tor_mmap_t *
+tor_mmap_file(const char *filename)
+{
+ char *res = read_file_to_str(filename, 1);
+ tor_mmap_t *handle;
+ if (! res)
+ return NULL;
+ handle = tor_malloc_zero(sizeof(tor_mmap_t));
+ handle->data = res;
+ handle->size = strlen(res) + 1;
+ return handle;
+}
+void
+tor_munmap_file(tor_mmap_t *handle)
+{
+ char *d = (char*)handle->data;
+ tor_free(d);
+ memset(handle, sizeof(tor_mmap_t), 0);
+ tor_free(handle);
+}
+#endif
+
/** Replacement for snprintf. Differs from platform snprintf in two
* ways: First, always NUL-terminates its output. Second, always
* returns -1 if the result is truncated. (Note that this return
@@ -206,7 +343,6 @@ tor_fix_source_file(const char *fname)
}
#endif
-#ifndef UNALIGNED_INT_ACCESS_OK
/**
* Read a 16-bit value beginning at <b>cp</b>. Equivalent to
* *(uint16_t*)(cp), but will not cause segfaults on platforms that forbid
@@ -249,7 +385,6 @@ set_uint32(char *cp, uint32_t v)
{
memcpy(cp,&v,4);
}
-#endif
/**
* Rename the file <b>from</b> to the file <b>to</b>. On unix, this is
@@ -294,8 +429,8 @@ touch_file(const char *fname)
void
set_socket_nonblocking(int socket)
{
-#ifdef MS_WINDOWS
- int nonblocking = 1;
+#if defined(MS_WINDOWS) && !defined(USE_BSOCKETS)
+ unsigned long nonblocking = 1;
ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking);
#else
fcntl(socket, F_SETFL, O_NONBLOCK);
@@ -322,10 +457,13 @@ set_socket_nonblocking(int socket)
int
tor_socketpair(int family, int type, int protocol, int fd[2])
{
-#ifdef HAVE_SOCKETPAIR
+//don't use win32 socketpairs (they are always bad)
+#if defined(HAVE_SOCKETPAIR) && !defined(MS_WINDOWS)
int r;
r = socketpair(family, type, protocol, fd);
return r < 0 ? -errno : r;
+#elif defined(USE_BSOCKETS)
+ return bsockepair(family, type, protocol, fd);
#else
/* This socketpair does not work when localhost is down. So
* it's really not the same thing at all. But it's close enough
@@ -356,17 +494,8 @@ tor_socketpair(int family, int type, int protocol, int fd[2])
}
listener = socket(AF_INET, type, 0);
- if (listener == -1)
+ if (listener < 0)
return -tor_socket_errno(-1);
- if (!SOCKET_IS_POLLABLE(listener)) {
- log_warn(LD_NET, "Too many connections; can't open socketpair");
- tor_close_socket(listener);
-#ifdef MS_WINDOWS
- return -ENFILE;
-#else
- return -ENCONN;
-#endif
- }
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
@@ -378,12 +507,8 @@ tor_socketpair(int family, int type, int protocol, int fd[2])
goto tidy_up_and_fail;
connector = socket(AF_INET, type, 0);
- if (connector == -1)
- goto tidy_up_and_fail;
- if (!SOCKET_IS_POLLABLE(connector)) {
- log_warn(LD_NET, "Too many connections; can't open socketpair");
+ if (connector < 0)
goto tidy_up_and_fail;
- }
/* We want to find out the port number to connect to. */
size = sizeof(connect_addr);
if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
@@ -396,12 +521,8 @@ tor_socketpair(int family, int type, int protocol, int fd[2])
size = sizeof(listen_addr);
acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
- if (acceptor == -1)
- goto tidy_up_and_fail;
- if (!SOCKET_IS_POLLABLE(acceptor)) {
- log_warn(LD_NET, "Too many connections; can't open socketpair");
+ if (acceptor < 0)
goto tidy_up_and_fail;
- }
if (size != sizeof(listen_addr))
goto abort_tidy_up_and_fail;
tor_close_socket(listener);
@@ -457,7 +578,7 @@ set_max_file_descriptors(unsigned long limit, unsigned long cap)
log_fn(LOG_INFO, LD_NET,
"This platform is missing getrlimit(). Proceeding.");
if (limit < cap) {
- log_info(LD_CONFIG, "ConnLimit must be at most %d. Using that.", cap);
+ log_info(LD_CONFIG, "ConnLimit must be at most %d. Using that.", (int)cap);
limit = cap;
}
#else
@@ -628,7 +749,7 @@ tor_lookup_hostname(const char *name, uint32_t *addr)
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
- err = getaddrinfo(name, NULL, NULL, &res);
+ err = getaddrinfo(name, NULL, &hints, &res);
if (!err) {
for (res_p = res; res_p; res_p = res_p->ai_next) {
if (res_p->ai_family == AF_INET) {
@@ -725,6 +846,7 @@ get_uname(void)
{ 3, 51, "Windows NT 3.51" },
{ -1, -1, NULL }
};
+#ifdef VER_SUITE_BACKOFFICE
static struct {
unsigned int mask; const char *str;
} win_mask_table[] = {
@@ -742,10 +864,10 @@ get_uname(void)
{ VER_SUITE_TERMINAL, " {terminal services}" },
{ 0, NULL },
};
+#endif
memset(&info, 0, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
if (! GetVersionEx((LPOSVERSIONINFO)&info)) {
- int err = GetLastError();
strlcpy(uname_result, "Bizarre version of Windows where GetVersionEx"
" doesn't work.", sizeof(uname_result));
uname_result_is_set = 1;
@@ -781,6 +903,7 @@ get_uname(void)
(int)info.dwMajorVersion,(int)info.dwMinorVersion,
info.szCSDVersion);
}
+#ifdef VER_SUITE_BACKOFFICE
if (info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
strlcat(uname_result, " [domain controller]", sizeof(uname_result));
} else if (info.wProductType == VER_NT_SERVER) {
@@ -800,6 +923,7 @@ get_uname(void)
tor_snprintf(uname_result+len, sizeof(uname_result)-len,
" {0x%x}", info.wSuiteMask);
}
+#endif
#else
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
#endif
@@ -818,14 +942,14 @@ get_uname(void)
* invoke them in a way pthreads would expect.
*/
typedef struct tor_pthread_data_t {
- int (*func)(void *);
+ void (*func)(void *);
void *data;
} tor_pthread_data_t;
static void *
tor_pthread_helper_fn(void *_data)
{
tor_pthread_data_t *data = _data;
- int (*func)(void*);
+ void (*func)(void*);
void *arg;
func = data->func;
arg = data->data;
@@ -845,7 +969,7 @@ tor_pthread_helper_fn(void *_data)
* running.
*/
int
-spawn_func(int (*func)(void *), void *data)
+spawn_func(void (*func)(void *), void *data)
{
#if defined(USE_WIN32_THREADS)
int rv;
@@ -888,6 +1012,10 @@ spawn_exit(void)
{
#if defined(USE_WIN32_THREADS)
_endthread();
+ //we should never get here. my compiler thinks that _endthread returns, this
+ //is an attempt to fool it.
+ tor_assert(0);
+ _exit(0);
#elif defined(USE_PTHREADS)
pthread_exit(NULL);
#else
@@ -895,6 +1023,7 @@ spawn_exit(void)
* call _exit, not exit, from child processes. */
_exit(0);
#endif
+
}
/** Set *timeval to the current time of day. On error, log and terminate.
@@ -1034,7 +1163,7 @@ tor_mutex_acquire(tor_mutex_t *m)
tor_assert(0);
break;
case WAIT_FAILED:
- log_warn(LD_GENERAL, "Failed to acquire mutex: %d", GetLastError());
+ log_warn(LD_GENERAL, "Failed to acquire mutex: %d",(int) GetLastError());
}
}
void
@@ -1043,7 +1172,7 @@ tor_mutex_release(tor_mutex_t *m)
BOOL r;
r = ReleaseMutex(m->handle);
if (!r) {
- log_warn(LD_GENERAL, "Failed to release mutex: %d", GetLastError());
+ log_warn(LD_GENERAL, "Failed to release mutex: %d", (int) GetLastError());
}
}
unsigned long
@@ -1110,7 +1239,7 @@ struct tor_mutex_t {
* should call tor_socket_errno <em>at most once</em> on the failing
* socket to get the error.
*/
-#ifdef MS_WINDOWS
+#if defined(MS_WINDOWS) && !defined(USE_BSOCKETS)
int
tor_socket_errno(int sock)
{
@@ -1126,7 +1255,7 @@ tor_socket_errno(int sock)
}
#endif
-#ifdef MS_WINDOWS
+#if defined(MS_WINDOWS) && !defined(USE_BSOCKETS)
#define E(code, s) { code, (s " [" #code " ]") }
struct { int code; const char *msg; } windows_socket_errors[] = {
E(WSAEINTR, "Interrupted function call"),
diff --git a/src/common/compat.h b/src/common/compat.h
index 7975dbb9ba..5567f5e9c1 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -76,6 +76,34 @@
#endif /* ifndef MAVE_MACRO__func__ */
#endif /* if not windows */
+#if defined(_MSC_VER) && (_MSC_VER < 1300)
+/* MSVC versions before 7 apparently don't believe that you can cast uint64_t
+ * to double and really mean it. */
+extern INLINE double U64_TO_DBL(uint64_t x) {
+ int64_t i = (int64_t) x;
+ return (i < 0) ? ((double) INT64_MAX) : (double) i;
+}
+#define DBL_TO_U64(x) ((uint64_t)(int64_t) (x))
+#else
+#define U64_TO_DBL(x) ((double) (x))
+#define DBL_TO_U64(x) ((uint64_t) (x))
+#endif
+
+/* GCC has several useful attributes. */
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define ATTR_NORETURN __attribute__((noreturn))
+#define ATTR_PURE __attribute__((pure))
+#define ATTR_MALLOC __attribute__((malloc))
+#define ATTR_NONNULL(x) __attribute__((nonnull x))
+#define PREDICT(exp, val) __builtin_expect((exp), (val))
+#else
+#define ATTR_NORETURN
+#define ATTR_PURE
+#define ATTR_MALLOC
+#define ATTR_NONNULL(x)
+#define PREDICT(exp, val) (exp)
+#endif
+
/* ===== String compatibility */
#ifdef MS_WINDOWS
/* Windows names string functions differently from most other platforms. */
@@ -83,13 +111,13 @@
#define strcasecmp stricmp
#endif
#ifndef HAVE_STRLCAT
-size_t strlcat(char *dst, const char *src, size_t siz);
+size_t strlcat(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2));
#endif
#ifndef HAVE_STRLCPY
-size_t strlcpy(char *dst, const char *src, size_t siz);
+size_t strlcpy(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2));
#endif
-#ifdef MS_WINDOWS
+#ifdef _MSC_VER
#define U64_PRINTF_ARG(a) (a)
#define U64_SCANF_ARG(a) (a)
#define U64_FORMAT "%I64u"
@@ -101,12 +129,22 @@ size_t strlcpy(char *dst, const char *src, size_t siz);
#define U64_LITERAL(n) (n ## llu)
#endif
+/** Opaque bookkeeping type used for mmap accounting. */
+typedef struct tor_mmap_t {
+ const char *data;
+ size_t size;
+} tor_mmap_t;
+
+tor_mmap_t *tor_mmap_file(const char *filename) ATTR_NONNULL((1));
+void tor_munmap_file(tor_mmap_t *handle) ATTR_NONNULL((1));
+
int tor_snprintf(char *str, size_t size, const char *format, ...)
- CHECK_PRINTF(3,4);
-int tor_vsnprintf(char *str, size_t size, const char *format, va_list args);
+ CHECK_PRINTF(3,4) ATTR_NONNULL((1,3));
+int tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
+ ATTR_NONNULL((1,3));
const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
- size_t nlen);
+ size_t nlen) ATTR_PURE ATTR_NONNULL((1,3));
#define TOR_ISALPHA(c) isalpha((int)(unsigned char)(c))
#define TOR_ISALNUM(c) isalnum((int)(unsigned char)(c))
@@ -114,9 +152,11 @@ const void *tor_memmem(const void *haystack, size_t hlen, const void *needle,
#define TOR_ISXDIGIT(c) isxdigit((int)(unsigned char)(c))
#define TOR_ISDIGIT(c) isdigit((int)(unsigned char)(c))
#define TOR_ISPRINT(c) isprint((int)(unsigned char)(c))
+#define TOR_ISLOWER(c) islower((int)(unsigned char)(c))
+#define TOR_ISUPPER(c) isupper((int)(unsigned char)(c))
-#define TOR_TOLOWER(c) (char)tolower((int)(unsigned char)(c))
-#define TOR_TOUPPER(c) (char)toupper((int)(unsigned char)(c))
+#define TOR_TOLOWER(c) ((char)tolower((int)(unsigned char)(c)))
+#define TOR_TOUPPER(c) ((char)toupper((int)(unsigned char)(c)))
#ifdef MS_WINDOWS
#define _SHORT_FILE_ (tor_fix_source_file(__FILE__))
@@ -159,8 +199,10 @@ int touch_file(const char *fname);
#endif
/* ===== Net compatibility */
-#ifdef MS_WINDOWS
-/** On windows, you have to call close() on fds returned by open(),
+#ifdef USE_BSOCKETS
+#define tor_close_socket(s) bclose(s)
+#elif defined(MS_WINDOWS)
+/** On Windows, you have to call close() on fds returned by open(),
* and closesocket() on fds returned by socket(). On Unix, everything
* gets close()'d. We abstract this difference by always using
* tor_close_socket to close sockets, and always using close() on
@@ -171,17 +213,21 @@ int touch_file(const char *fname);
#define tor_close_socket(s) close(s)
#endif
+#ifdef USE_BSOCKETS
+#define tor_socket_send(s, buf, len, flags) bsend(s, buf, len, flags)
+#define tor_socket_recv(s, buf, len, flags) brecv(s, buf, len, flags)
+#else
+#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
+#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
+#endif
+
#if (SIZEOF_SOCKLEN_T == 0)
typedef int socklen_t;
#endif
-/* Now that we use libevent, all real sockets are safe for polling ... or
- * if they aren't, libevent will help us. */
-#define SOCKET_IS_POLLABLE(fd) ((fd)>=0)
-
struct in_addr;
-int tor_inet_aton(const char *cp, struct in_addr *addr);
-int tor_lookup_hostname(const char *name, uint32_t *addr);
+int tor_inet_aton(const char *cp, struct in_addr *addr) ATTR_NONNULL((1,2));
+int tor_lookup_hostname(const char *name, uint32_t *addr) ATTR_NONNULL((1,2));
void set_socket_nonblocking(int socket);
int tor_socketpair(int family, int type, int protocol, int fd[2]);
int network_init(void);
@@ -191,7 +237,7 @@ int network_init(void);
* errnos against expected values, and use tor_socket_errno to find
* the actual errno after a socket operation fails.
*/
-#ifdef MS_WINDOWS
+#if defined(MS_WINDOWS) && !defined(USE_BSOCKETS)
/** Return true if e is EAGAIN or the local equivalent. */
#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == WSAEWOULDBLOCK)
/** Return true if e is EINPROGRESS or the local equivalent. */
@@ -226,21 +272,10 @@ const char *tor_socket_strerror(int e);
/* ===== OS compatibility */
const char *get_uname(void);
-/* Some platforms segfault when you try to access a multi-byte type
- * that isn't aligned to a word boundary. The macros and/or functions
- * below can be used to access unaligned data on any platform.
- */
-#ifdef UNALIGNED_INT_ACCESS_OK
-#define get_uint16(cp) (*(uint16_t*)(cp))
-#define get_uint32(cp) (*(uint32_t*)(cp))
-#define set_uint16(cp,v) do { *(uint16_t*)(cp) = (v); } while (0)
-#define set_uint32(cp,v) do { *(uint32_t*)(cp) = (v); } while (0)
-#else
-uint16_t get_uint16(const char *cp);
-uint32_t get_uint32(const char *cp);
-void set_uint16(char *cp, uint16_t v);
-void set_uint32(char *cp, uint32_t v);
-#endif
+uint16_t get_uint16(const char *cp) ATTR_PURE ATTR_NONNULL((1));
+uint32_t get_uint32(const char *cp) ATTR_PURE ATTR_NONNULL((1));
+void set_uint16(char *cp, uint16_t v) ATTR_NONNULL((1));
+void set_uint32(char *cp, uint32_t v) ATTR_NONNULL((1));
int set_max_file_descriptors(unsigned long limit, unsigned long cap);
int switch_id(char *user, char *group);
@@ -248,8 +283,8 @@ int switch_id(char *user, char *group);
char *get_user_homedir(const char *username);
#endif
-int spawn_func(int (*func)(void *), void *data);
-void spawn_exit(void);
+int spawn_func(void (*func)(void *), void *data);
+void spawn_exit(void) ATTR_NORETURN;
#if defined(ENABLE_THREADS) && defined(MS_WINDOWS)
#define USE_WIN32_THREADS
@@ -262,8 +297,9 @@ void spawn_exit(void);
#undef TOR_IS_MULTITHREADED
#endif
-/* Because we use threads instead of processes on Windows, we need locking on
- * Windows. On Unixy platforms, these functions are no-ops. */
+/* Because we use threads instead of processes on most platforms (Windows,
+ * Linux, etc), we need locking for them. On platforms with poor thread
+ * support or broken gethostbyname_r, these functions are no-ops. */
typedef struct tor_mutex_t tor_mutex_t;
#ifdef TOR_IS_MULTITHREADED
@@ -280,5 +316,20 @@ unsigned long tor_get_thread_id(void);
#define tor_get_thread_id() (1UL)
#endif
+/*for some reason my compiler doesn't have these version flags defined
+ a nice homework assignment for someone one day is to define the rest*/
+//these are the values as given on MSDN
+#ifdef MS_WINDOWS
+
+#ifndef VER_SUITE_EMBEDDEDNT
+#define VER_SUITE_EMBEDDEDNT 0x00000040
+#endif
+
+#ifndef VER_SUITE_SINGLEUSERTS
+#define VER_SUITE_SINGLEUSERTS 0x00000100
+#endif
+
+#endif
+
#endif
diff --git a/src/common/container.c b/src/common/container.c
index 33a77cd42c..64ad420633 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -50,6 +50,7 @@ smartlist_create(void)
void
smartlist_free(smartlist_t *sl)
{
+ tor_assert(sl != NULL);
tor_free(sl->list);
tor_free(sl);
}
@@ -127,6 +128,32 @@ smartlist_remove(smartlist_t *sl, const void *element)
}
}
+/** If <b>sl</b> is nonempty, remove and return the final element. Otherwise,
+ * return NULL. */
+void *
+smartlist_pop_last(smartlist_t *sl)
+{
+ tor_assert(sl);
+ if (sl->num_used)
+ return sl->list[--sl->num_used];
+ else
+ return NULL;
+}
+
+/** Reverse the order of the items in <b>sl</b>. */
+void
+smartlist_reverse(smartlist_t *sl)
+{
+ int i, j;
+ void *tmp;
+ tor_assert(sl);
+ for (i = 0, j = sl->num_used-1; i < j; ++i, --j) {
+ tmp = sl->list[i];
+ sl->list[i] = sl->list[j];
+ sl->list[j] = tmp;
+ }
+}
+
/** If there are any strings in sl equal to element, remove and free them.
* Does not preserve order. */
void
@@ -402,6 +429,29 @@ smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b))
(int (*)(const void *,const void*))compare);
}
+/** Given a sorted smartlist <b>sl</b> and the comparison function used to
+ * sort it, remove all duplicate members. If free_fn is provided, calls
+ * free_fn on each duplicate. Otherwise, frees them with tor_free(), which
+ * may not be what you want.. Preserves order.
+ */
+void
+smartlist_uniq(smartlist_t *sl,
+ int (*compare)(const void **a, const void **b),
+ void (*free_fn)(void *a))
+{
+ int i;
+ for (i=1; i < sl->num_used; ++i) {
+ if (compare((const void **)&(sl->list[i-1]),
+ (const void **)&(sl->list[i])) == 0) {
+ if (free_fn)
+ free_fn(sl->list[i]);
+ else
+ tor_free(sl->list[i]);
+ smartlist_del_keeporder(sl, i--);
+ }
+ }
+}
+
/** Assuming the members of <b>sl</b> are in order, return a pointer to the
* member which matches <b>key</b>. Ordering and matching are defined by a
* <b>compare</b> function, which returns 0 on a match; less than 0 if key is
@@ -435,6 +485,117 @@ smartlist_sort_strings(smartlist_t *sl)
smartlist_sort(sl, _compare_string_ptrs);
}
+/** Remove duplicate strings from a sorted list, and free them with tor_free().
+ */
+void
+smartlist_uniq_strings(smartlist_t *sl)
+{
+ smartlist_uniq(sl, _compare_string_ptrs, NULL);
+}
+
+#define LEFT_CHILD(i) ( ((i)+1)*2 - 1)
+#define RIGHT_CHILD(i) ( ((i)+1)*2 )
+#define PARENT(i) ( ((i)+1)/2 - 1)
+
+static INLINE void
+smartlist_heapify(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b),
+ int idx)
+{
+ while (1) {
+ int left_idx = LEFT_CHILD(idx);
+ int best_idx;
+
+ if (left_idx >= sl->num_used)
+ return;
+ if (compare(sl->list[idx],sl->list[left_idx]) < 0)
+ best_idx = idx;
+ else
+ best_idx = left_idx;
+ if (left_idx+1 < sl->num_used &&
+ compare(sl->list[left_idx+1],sl->list[best_idx]) < 0)
+ best_idx = left_idx + 1;
+
+ if (best_idx == idx) {
+ return;
+ } else {
+ void *tmp = sl->list[idx];
+ sl->list[idx] = sl->list[best_idx];
+ sl->list[best_idx] = tmp;
+
+ idx = best_idx;
+ }
+ }
+}
+
+void
+smartlist_pqueue_add(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b),
+ void *item)
+{
+ int idx;
+ smartlist_add(sl,item);
+
+ for (idx = sl->num_used - 1; idx; ) {
+ int parent = PARENT(idx);
+ if (compare(sl->list[idx], sl->list[parent]) < 0) {
+ void *tmp = sl->list[parent];
+ sl->list[parent] = sl->list[idx];
+ sl->list[idx] = tmp;
+ idx = parent;
+ } else {
+ return;
+ }
+ }
+}
+
+void *
+smartlist_pqueue_pop(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b))
+{
+ void *top;
+ tor_assert(sl->num_used);
+
+ top = sl->list[0];
+ if (--sl->num_used) {
+ sl->list[0] = sl->list[sl->num_used];
+ smartlist_heapify(sl, compare, 0);
+ }
+ return top;
+}
+
+void
+smartlist_pqueue_assert_ok(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b))
+{
+ int i;
+ for (i = sl->num_used - 1; i > 0; --i) {
+ tor_assert(compare(sl->list[PARENT(i)], sl->list[i]) <= 0);
+ }
+}
+
+/** Helper: compare two DIGEST_LEN digests. */
+static int
+_compare_digests(const void **_a, const void **_b)
+{
+ return memcmp((const char*)*_a, (const char*)*_b, DIGEST_LEN);
+}
+
+/** Sort the list of DIGEST_LEN-byte digests into ascending order. */
+void
+smartlist_sort_digests(smartlist_t *sl)
+{
+ smartlist_sort(sl, _compare_digests);
+}
+
+/** Remove duplicate digests from a sorted list, and free them with tor_free().
+ */
+void
+smartlist_uniq_digests(smartlist_t *sl)
+{
+ smartlist_uniq(sl, _compare_digests, NULL);
+}
+
#define DEFINE_MAP_STRUCTS(maptype, keydecl, prefix) \
typedef struct prefix ## entry_t { \
HT_ENTRY(prefix ## entry_t) node; \
@@ -702,7 +863,7 @@ strmap_remove_lc(strmap_t *map, const char *key)
* iter = strmap_iter_next_rmv(iter);
* free(val);
* } else {
- * for (;*cp;cp++) *cp = toupper(*cp);
+ * for (;*cp;cp++) *cp = TOR_TOUPPER(*cp);
* iter = strmap_iter_next(iter);
* }
* }
@@ -844,6 +1005,17 @@ digestmap_free(digestmap_t *map, void (*free_val)(void*))
tor_free(map);
}
+void
+strmap_assert_ok(strmap_t *map)
+{
+ tor_assert(!_strmap_impl_HT_REP_IS_BAD(&map->head));
+}
+void
+digestmap_assert_ok(digestmap_t *map)
+{
+ tor_assert(!_digestmap_impl_HT_REP_IS_BAD(&map->head));
+}
+
/** Return true iff <b>map</b> has no entries. */
int
strmap_isempty(strmap_t *map)
diff --git a/src/common/container.h b/src/common/container.h
index 83c0f28229..5f0417cf4d 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -29,24 +29,29 @@ void smartlist_clear(smartlist_t *sl);
void smartlist_add(smartlist_t *sl, void *element);
void smartlist_add_all(smartlist_t *sl, const smartlist_t *s2);
void smartlist_remove(smartlist_t *sl, const void *element);
+void *smartlist_pop_last(smartlist_t *sl);
+void smartlist_reverse(smartlist_t *sl);
void smartlist_string_remove(smartlist_t *sl, const char *element);
-int smartlist_isin(const smartlist_t *sl, const void *element);
-int smartlist_string_isin(const smartlist_t *sl, const char *element);
-int smartlist_string_num_isin(const smartlist_t *sl, int num);
-int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2);
+int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE;
+int smartlist_string_isin(const smartlist_t *sl, const char *element)
+ ATTR_PURE;
+int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE;
+int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2)
+ ATTR_PURE;
void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2);
void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2);
+
/* smartlist_choose() is defined in crypto.[ch] */
#ifdef DEBUG_SMARTLIST
/** Return the number of items in sl.
*/
-extern INLINE int smartlist_len(const smartlist_t *sl) {
+extern INLINE int smartlist_len(const smartlist_t *sl) ATTR_PURE {
tor_assert(sl);
return (sl)->num_used;
}
/** Return the <b>idx</b>th element of sl.
*/
-extern INLINE void *smartlist_get(const smartlist_t *sl, int idx) {
+extern INLINE void *smartlist_get(const smartlist_t *sl, int idx) ATTR_PURE {
tor_assert(sl);
tor_assert(idx>=0);
tor_assert(sl->num_used < idx);
@@ -69,18 +74,34 @@ void smartlist_del_keeporder(smartlist_t *sl, int idx);
void smartlist_insert(smartlist_t *sl, int idx, void *val);
void smartlist_sort(smartlist_t *sl,
int (*compare)(const void **a, const void **b));
+void smartlist_uniq(smartlist_t *sl,
+ int (*compare)(const void **a, const void **b),
+ void (*free_fn)(void *elt));
void smartlist_sort_strings(smartlist_t *sl);
+void smartlist_sort_digests(smartlist_t *sl);
+void smartlist_uniq_strings(smartlist_t *sl);
+void smartlist_uniq_digests(smartlist_t *sl);
void *smartlist_bsearch(smartlist_t *sl, const void *key,
- int (*compare)(const void *key, const void **member));
+ int (*compare)(const void *key, const void **member))
+ ATTR_PURE;
+
+void smartlist_pqueue_add(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b),
+ void *item);
+void *smartlist_pqueue_pop(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b));
+void smartlist_pqueue_assert_ok(smartlist_t *sl,
+ int (*compare)(const void *a, const void *b));
#define SPLIT_SKIP_SPACE 0x01
#define SPLIT_IGNORE_BLANK 0x02
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep,
int flags, int max);
char *smartlist_join_strings(smartlist_t *sl, const char *join, int terminate,
- size_t *len_out);
+ size_t *len_out) ATTR_MALLOC;
char *smartlist_join_strings2(smartlist_t *sl, const char *join,
- size_t join_len, int terminate, size_t *len_out);
+ size_t join_len, int terminate, size_t *len_out)
+ ATTR_MALLOC;
/** Iterate over the items in a smartlist <b>sl</b>, in order. For each item,
* assign it to a new local variable of type <b>type</b> named <b>var</b>, and
@@ -122,7 +143,8 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join,
prefix##iter_t *prefix##iter_next(maptype *map, prefix##iter_t *iter); \
prefix##iter_t *prefix##iter_next_rmv(maptype *map, prefix##iter_t *iter); \
void prefix##iter_get(prefix##iter_t *iter, keytype *keyp, void **valp); \
- int prefix##iter_done(prefix##iter_t *iter);
+ int prefix##iter_done(prefix##iter_t *iter); \
+ void prefix##assert_ok(maptype *map);
/* Map from const char * to void *. Implemented with a hash table. */
DECLARE_MAP_FNS(strmap_t, const char *, strmap_);
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 62e7c3c245..57b504f5ba 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -189,6 +189,10 @@ crypto_global_init(int useAccel)
OpenSSL_add_all_algorithms();
_crypto_global_initialized = 1;
setup_openssl_threading();
+ /* XXX the below is a bug, since we can't know if we're supposed
+ * to be using hardware acceleration or not. we should arrange
+ * for this function to be called before init_keys. But make it
+ * not complain loudly, at least until we make acceleration work. */
if (useAccel < 0) {
log_info(LD_CRYPTO, "Initializing OpenSSL via tor_tls_init().");
}
@@ -499,13 +503,13 @@ crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest,
}
BIO_get_mem_ptr(b, &buf);
- BIO_set_close(b, BIO_NOCLOSE); /* so BIO_free doesn't free buf */
+ (void)BIO_set_close(b, BIO_NOCLOSE); /* so BIO_free doesn't free buf */
BIO_free(b);
tor_assert(buf->length >= 0);
*dest = tor_malloc(buf->length+1);
memcpy(*dest, buf->data, buf->length);
- (*dest)[buf->length] = 0; /* null terminate it */
+ (*dest)[buf->length] = 0; /* nul terminate it */
*len = buf->length;
BUF_MEM_free(buf);
@@ -575,70 +579,6 @@ crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env,
return r;
}
-/** Allocate a new string in *<b>out</b>, containing the public portion of the
- * RSA key in <b>env</b>, encoded first with DER, then in base-64. Return the
- * length of the encoded representation on success, and -1 on failure.
- *
- * <i>This function is for temporary use only. We need a simple
- * one-line representation for keys to work around a bug in parsing
- * directories containing "opt keyword\n-----BEGIN OBJECT----" entries
- * in versions of Tor up to 0.0.9pre2.</i>
- */
-int
-crypto_pk_DER64_encode_public_key(crypto_pk_env_t *env, char **out)
-{
- int len;
- char buf[PK_BYTES*2]; /* Too long, but hey, stacks are big. */
- tor_assert(env);
- tor_assert(out);
- len = crypto_pk_asn1_encode(env, buf, sizeof(buf));
- if (len < 0) {
- return -1;
- }
- *out = tor_malloc(len * 2); /* too long, but safe. */
- if (base64_encode(*out, len*2, buf, len) < 0) {
- log_warn(LD_CRYPTO, "Error base64-encoding DER-encoded key");
- tor_free(*out);
- return -1;
- }
- /* Remove spaces */
- tor_strstrip(*out, " \r\n\t");
- return strlen(*out);
-}
-
-/** Decode a base-64 encoded DER representation of an RSA key from <b>in</b>,
- * and store the result in <b>env</b>. Return 0 on success, -1 on failure.
- *
- * <i>This function is for temporary use only. We need a simple
- * one-line representation for keys to work around a bug in parsing
- * directories containing "opt keyword\n-----BEGIN OBJECT----" entries
- * in versions of Tor up to 0.0.9pre2.</i>
- */
-crypto_pk_env_t *
-crypto_pk_DER64_decode_public_key(const char *in)
-{
- char partitioned[PK_BYTES*2 + 16];
- char buf[PK_BYTES*2];
- int len;
- tor_assert(in);
- len = strlen(in);
-
- if (strlen(in) > PK_BYTES*2) {
- return NULL;
- }
- /* base64_decode doesn't work unless we insert linebreaks every 64
- * characters. how dumb. */
- if (tor_strpartition(partitioned, sizeof(partitioned), in, "\n", 64,
- ALWAYS_TERMINATE))
- return NULL;
- len = base64_decode(buf, sizeof(buf), partitioned, strlen(partitioned));
- if (len<0) {
- log_warn(LD_CRYPTO,"Error base-64 decoding key");
- return NULL;
- }
- return crypto_pk_asn1_decode(buf, len);
-}
-
/** Return true iff <b>env</b> has a valid key.
*/
int
@@ -1866,6 +1806,8 @@ secret_to_key(char *key_out, size_t key_out_len, const char *secret,
static void
_openssl_locking_cb(int mode, int n, const char *file, int line)
{
+ (void)file;
+ (void)line;
if (!_openssl_mutexes)
/* This is not a really good fix for the
* "release-freed-lock-from-separate-thread-on-shutdown" problem, but
diff --git a/src/common/crypto.h b/src/common/crypto.h
index df112a1d8e..050849cfe5 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -79,8 +79,6 @@ int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env,
const char *src, size_t len);
int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env,
const char *fname);
-int crypto_pk_DER64_encode_public_key(crypto_pk_env_t *env, char **dest);
-crypto_pk_env_t *crypto_pk_DER64_decode_public_key(const char *in);
int crypto_pk_check_key(crypto_pk_env_t *env);
int crypto_pk_cmp_keys(crypto_pk_env_t *a, crypto_pk_env_t *b);
diff --git a/src/common/ht.h b/src/common/ht.h
index dd90f2e4fd..a83e093264 100644
--- a/src/common/ht.h
+++ b/src/common/ht.h
@@ -99,7 +99,7 @@ ht_string_hash(const char *s)
#define HT_PROTOTYPE(name, type, field, hashfn, eqfn) \
int name##_HT_GROW(struct name *ht, unsigned min_capacity); \
void name##_HT_CLEAR(struct name *ht); \
- int _##name##_HT_REP_OK(struct name *ht); \
+ int _##name##_HT_REP_IS_BAD(struct name *ht); \
/* Helper: returns a pointer to the right location in the table \
* 'head' to find or insert the element 'elm'. */ \
static INLINE struct type ** \
@@ -187,7 +187,6 @@ ht_string_hash(const char *s)
int (*fn)(struct type *, void *), \
void *data) \
{ \
- /* XXXX use tricks to prevent concurrent mod? */ \
unsigned idx; \
int remove; \
struct type **p, **nextp, *next; \
@@ -261,77 +260,6 @@ ht_string_hash(const char *s)
} \
}
-#if 0
-/* Helpers for an iterator type that saves some mod operations at the expense
- * of many branches. Not worth it, it seems. */
-
-#define HT_ITER(type) \
- struct type##_ITER { \
- struct type **hti_nextp; \
- unsigned hti_bucket; \
- }
-
- static INLINE void \
- name##_HT_ITER_START(struct name *head, struct type##_ITER *iter) \
- { \
- /* XXXX Magic to stop modifications? */ \
- iter->hti_bucket = 0; \
- while (iter->hti_bucket < head->hth_table_length) { \
- iter->hti_nextp = &head->hth_table[iter->hti_bucket]; \
- if (*iter->hti_nextp) \
- return; \
- ++iter->hti_bucket; \
- } \
- iter->hti_nextp = NULL; \
- } \
- static INLINE int \
- name##_HT_ITER_DONE(struct name *head, struct type##_ITER *iter) \
- { \
- return iter->hti_nextp == NULL; \
- } \
- static INLINE struct type * \
- name##_HT_ITER_GET(struct name *head, struct type##_ITER *iter) \
- { \
- return *iter->hti_nextp; \
- } \
- static INLINE void \
- name##_HT_ITER_NEXT(struct name *head, struct type##_ITER *iter) \
- { \
- if (!iter->hti_nextp) \
- return; \
- if ((*iter->hti_nextp)->field.hte_next) { \
- iter->hti_nextp = &(*iter->hti_nextp)->field.hte_next; \
- return; \
- } \
- while (++iter->hti_bucket < head->hth_table_length) { \
- iter->hti_nextp = &head->hth_table[iter->hti_bucket]; \
- if (*iter->hti_nextp) \
- return; \
- ++iter->hti_bucket; \
- } \
- iter->hti_nextp = NULL; \
- } \
- static INLINE void \
- name##_HT_ITER_NEXT_RMV(struct name *head, struct type##_ITER *iter) \
- { \
- if (!iter->hti_nextp) \
- return; \
- --head->hth_n_entries; \
- if ((*iter->hti_nextp)->field.hte_next) { \
- *iter->hti_nextp = (*iter->hti_nextp)->field.hte_next; \
- if (*iter->hti_nextp) \
- return; \
- } \
- while (++iter->hti_bucket < head->hth_table_length) { \
- iter->hti_nextp = &head->hth_table[iter->hti_bucket]; \
- if (*iter->hti_nextp) \
- return; \
- ++iter->hti_bucket; \
- } \
- iter->hti_nextp = NULL; \
- }
-#endif
-
#define HT_GENERATE(name, type, field, hashfn, eqfn, load, mallocfn, \
reallocfn, freefn) \
static unsigned name##_PRIMES[] = { \
@@ -417,38 +345,41 @@ ht_string_hash(const char *s)
head->hth_table_length = 0; \
HT_INIT(head); \
} \
- /* Debugging helper: return true iff the representation of 'head' is \
+ /* Debugging helper: return false iff the representation of 'head' is \
* internally consistent. */ \
int \
- _##name##_HT_REP_OK(struct name *head) \
+ _##name##_HT_REP_IS_BAD(struct name *head) \
{ \
unsigned n, i; \
struct type *elm; \
if (!head->hth_table_length) { \
- return !head->hth_table && !head->hth_n_entries && \
- !head->hth_load_limit && head->hth_prime_idx == -1; \
+ if (!head->hth_table && !head->hth_n_entries && \
+ !head->hth_load_limit && head->hth_prime_idx == -1) \
+ return 0; \
+ else \
+ return 1; \
} \
if (!head->hth_table || head->hth_prime_idx < 0 || \
!head->hth_load_limit) \
- return 0; \
+ return 2; \
if (head->hth_n_entries > head->hth_load_limit) \
- return 0; \
+ return 3; \
if (head->hth_table_length != name##_PRIMES[head->hth_prime_idx]) \
- return 0; \
+ return 4; \
if (head->hth_load_limit != (unsigned)(load*head->hth_table_length)) \
- return 0; \
+ return 5; \
for (n = i = 0; i < head->hth_table_length; ++i) { \
for (elm = head->hth_table[i]; elm; elm = elm->field.hte_next) { \
if (elm->field.hte_hash != hashfn(elm)) \
- return 0; \
+ return 1000 + i; \
if ((elm->field.hte_hash % head->hth_table_length) != i) \
- return 0; \
+ return 10000 + i; \
++n; \
} \
} \
if (n != head->hth_n_entries) \
- return 0; \
- return 1; \
+ return 6; \
+ return 0; \
}
/*
diff --git a/src/common/log.c b/src/common/log.c
index 677cb5241c..1d9c10d565 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -327,9 +327,12 @@ close_logs(void)
}
/** Remove and free the log entry <b>victim</b> from the linked-list
- * logfiles (it must be present in the list when this function is
- * called). After this function is called, the caller shouldn't refer
- * to <b>victim</b> anymore.
+ * logfiles (it is probably present, but it might not be due to thread
+ * racing issues). After this function is called, the caller shouldn't
+ * refer to <b>victim</b> anymore.
+ *
+ * Long-term, we need to do something about races in the log subsystem
+ * in general. See bug 222 for more details.
*/
static void
delete_log(logfile_t *victim)
diff --git a/src/common/log.h b/src/common/log.h
index 1b4c96e8fa..8122a94aa6 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -23,14 +23,12 @@
"We aren't prepared to deal with that."
#endif
#else
-/* XXXX Note: The code was originally written to refer to severities,
- * with 0 being the least severe; while syslog's logging code refers to
- * priorities, with 0 being the most important. Thus, all our comparisons
- * needed to be reversed when we added syslog support.
+/* Note: Syslog's logging code refers to priorities, with 0 being the most
+ * important. Thus, all our comparisons needed to be reversed when we added
+ * syslog support.
*
- * The upshot of this is that comments about log levels may be messed
- * up: for "maximum severity" read "most severe" and "numerically
- * *lowest* severity".
+ * The upshot of this is that comments about log levels may be messed up: for
+ * "maximum severity" read "most severe" and "numerically *lowest* severity".
*/
/** Debug-level severity: for hyper-verbose messages of no interest to
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index 12cc7ec969..ec02870776 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -15,7 +15,7 @@ const char torgzip_c_id[] =
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
-#ifdef MS_WINDOWS
+#ifdef _MSC_VER
#include "..\..\contrib\zlib\zlib.h"
#else
#include <zlib.h>
@@ -282,3 +282,97 @@ detect_compression_method(const char *in, size_t in_len)
}
}
+struct tor_zlib_state_t {
+ struct z_stream_s stream;
+ int compress;
+};
+
+/** DOCDOC */
+tor_zlib_state_t *
+tor_zlib_new(int compress, compress_method_t method)
+{
+ tor_zlib_state_t *out;
+
+ if (method == GZIP_METHOD && !is_gzip_supported()) {
+ /* Old zlib version don't support gzip in inflateInit2 */
+ log_warn(LD_GENERAL, "Gzip not supported with zlib %s", ZLIB_VERSION);
+ return NULL;
+ }
+
+ out = tor_malloc_zero(sizeof(tor_zlib_state_t));
+ out->stream.zalloc = Z_NULL;
+ out->stream.zfree = Z_NULL;
+ out->stream.opaque = NULL;
+ out->compress = compress;
+ if (compress) {
+ if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
+ method_bits(method), 8, Z_DEFAULT_STRATEGY) != Z_OK)
+ goto err;
+ } else {
+ if (inflateInit2(&out->stream, method_bits(method)) != Z_OK)
+ goto err;
+ }
+ return out;
+
+ err:
+ tor_free(out);
+ return NULL;
+}
+
+/** DOCDOC */
+tor_zlib_output_t
+tor_zlib_process(tor_zlib_state_t *state,
+ char **out, size_t *out_len,
+ const char **in, size_t *in_len,
+ int finish)
+{
+ int err;
+ state->stream.next_in = (unsigned char*) *in;
+ state->stream.avail_in = *in_len;
+ state->stream.next_out = (unsigned char*) *out;
+ state->stream.avail_out = *out_len;
+
+ if (state->compress) {
+ err = deflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
+ } else {
+ err = inflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
+ }
+
+ *out = (char*) state->stream.next_out;
+ *out_len = state->stream.avail_out;
+ *in = (const char *) state->stream.next_in;
+ *in_len = state->stream.avail_in;
+
+ switch (err)
+ {
+ case Z_STREAM_END:
+ return TOR_ZLIB_DONE;
+ case Z_BUF_ERROR:
+ if (state->stream.avail_in == 0)
+ return Z_OK;
+ return TOR_ZLIB_BUF_FULL;
+ case Z_OK:
+ if (state->stream.avail_out == 0 || finish)
+ return TOR_ZLIB_BUF_FULL;
+ return TOR_ZLIB_OK;
+ default:
+ log_warn(LD_GENERAL, "Gzip returned an error: %s",
+ state->stream.msg ? state->stream.msg : "<no message>");
+ return TOR_ZLIB_ERR;
+ }
+}
+
+/** DOCDOC */
+void
+tor_zlib_free(tor_zlib_state_t *state)
+{
+ tor_assert(state);
+
+ if (state->compress)
+ deflateEnd(&state->stream);
+ else
+ inflateEnd(&state->stream);
+
+ tor_free(state);
+}
+
diff --git a/src/common/torgzip.h b/src/common/torgzip.h
index 134ef03268..153ab4992d 100644
--- a/src/common/torgzip.h
+++ b/src/common/torgzip.h
@@ -31,5 +31,17 @@ int is_gzip_supported(void);
int detect_compression_method(const char *in, size_t in_len);
+typedef enum {
+ TOR_ZLIB_OK, TOR_ZLIB_DONE, TOR_ZLIB_BUF_FULL, TOR_ZLIB_ERR
+} tor_zlib_output_t;
+typedef struct tor_zlib_state_t tor_zlib_state_t;
+tor_zlib_state_t *tor_zlib_new(int compress, compress_method_t method);
+
+tor_zlib_output_t tor_zlib_process(tor_zlib_state_t *state,
+ char **out, size_t *out_len,
+ const char **in, size_t *in_len,
+ int finish);
+void tor_zlib_free(tor_zlib_state_t *state);
+
#endif
diff --git a/src/common/torint.h b/src/common/torint.h
index 8202baa597..b69af3eeae 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -155,6 +155,9 @@ typedef unsigned long long uint64_t;
#ifndef UINT64_MAX
#define UINT64_MAX 0xffffffffffffffffull
#endif
+#ifndef INT64_MAX
+#define INT64_MAX 0x7fffffffffffffffll
+#endif
#endif
#if (SIZEOF___INT64 == 8)
@@ -169,6 +172,9 @@ typedef unsigned __int64 uint64_t;
#ifndef UINT64_MAX
#define UINT64_MAX 0xffffffffffffffffui64
#endif
+#ifndef INT64_MAX
+#define INT64_MAX 0x7fffffffffffffffi64
+#endif
#endif
#if (SIZEOF_VOID_P > 4 && SIZEOF_VOID_P <= 8)
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 4401185c59..d9e71a6380 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -40,7 +40,6 @@ const char tortls_c_id[] =
/* DOCDOC */
typedef struct tor_tls_context_t {
SSL_CTX *ctx;
- SSL_CTX *client_only_ctx;
} tor_tls_context_t;
/** Holds a SSL object and its associated data. Members are only
@@ -170,7 +169,6 @@ tor_tls_free_all(void)
{
if (global_tls_context) {
SSL_CTX_free(global_tls_context->ctx);
- SSL_CTX_free(global_tls_context->client_only_ctx);
tor_free(global_tls_context);
global_tls_context = NULL;
}
@@ -234,7 +232,7 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
if ((nid = OBJ_txt2nid("organizationName")) == NID_undef)
goto error;
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
- (unsigned char*)"TOR", -1, -1, 0)))
+ (unsigned char*)"Tor", -1, -1, 0)))
goto error;
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
@@ -248,7 +246,7 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
if ((nid = OBJ_txt2nid("organizationName")) == NID_undef)
goto error;
if (!(X509_NAME_add_entry_by_NID(name_issuer, nid, MBSTRING_ASC,
- (unsigned char*)"TOR", -1, -1, 0)))
+ (unsigned char*)"Tor", -1, -1, 0)))
goto error;
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
if (!(X509_NAME_add_entry_by_NID(name_issuer, nid, MBSTRING_ASC,
@@ -301,20 +299,16 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
#define CIPHER_LIST SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA
#endif
-/** Create a new TLS context. If we are going to be using it as a
- * server, it must have isServer set to true, <b>identity</b> set to the
- * identity key used to sign that certificate, and <b>nickname</b> set to
- * the server's nickname. If we're only going to be a client,
- * isServer should be false, identity should be NULL, and nickname
- * should be NULL. Return -1 if failure, else 0.
+/** Create a new TLS context for use with Tor TLS handshakes.
+ * <b>identity</b> should be set to the identity key used to sign the
+ * certificate, and <b>nickname</b> set to the nickname to use.
*
* You can call this function multiple times. Each time you call it,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
int
-tor_tls_context_new(crypto_pk_env_t *identity,
- int isServer, const char *nickname,
+tor_tls_context_new(crypto_pk_env_t *identity, const char *nickname,
unsigned int key_lifetime)
{
crypto_pk_env_t *rsa = NULL;
@@ -323,86 +317,71 @@ tor_tls_context_new(crypto_pk_env_t *identity,
tor_tls_context_t *result = NULL;
X509 *cert = NULL, *idcert = NULL;
char nn2[128];
- int client_only;
- SSL_CTX **ctx;
if (!nickname)
nickname = "null";
tor_snprintf(nn2, sizeof(nn2), "%s <identity>", nickname);
tor_tls_init();
- if (isServer) {
- /* Generate short-term RSA key. */
- if (!(rsa = crypto_new_pk_env()))
- goto error;
- if (crypto_pk_generate_key(rsa)<0)
- goto error;
- /* Create certificate signed by identity key. */
- cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
- key_lifetime);
- /* Create self-signed certificate for identity key. */
- idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
- IDENTITY_CERT_LIFETIME);
- if (!cert || !idcert) {
- log(LOG_WARN, LD_CRYPTO, "Error creating certificate");
- goto error;
- }
+ /* Generate short-term RSA key. */
+ if (!(rsa = crypto_new_pk_env()))
+ goto error;
+ if (crypto_pk_generate_key(rsa)<0)
+ goto error;
+ /* Create certificate signed by identity key. */
+ cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
+ key_lifetime);
+ /* Create self-signed certificate for identity key. */
+ idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
+ IDENTITY_CERT_LIFETIME);
+ if (!cert || !idcert) {
+ log(LOG_WARN, LD_CRYPTO, "Error creating certificate");
+ goto error;
}
result = tor_malloc(sizeof(tor_tls_context_t));
- result->ctx = result->client_only_ctx = NULL;
- for (client_only=0; client_only <= 1; ++client_only) {
- ctx = client_only ? &result->client_only_ctx : &result->ctx;
#ifdef EVERYONE_HAS_AES
- /* Tell OpenSSL to only use TLS1 */
- if (!(*ctx = SSL_CTX_new(TLSv1_method())))
- goto error;
+ /* Tell OpenSSL to only use TLS1 */
+ if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
+ goto error;
#else
- /* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */
- if (!(*ctx = SSL_CTX_new(SSLv23_method())))
- goto error;
- SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
-#endif
-#ifndef ENABLE_0119_PARANOIA_A
- SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE);
+ /* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */
+ if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
+ goto error;
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
#endif
- if (!SSL_CTX_set_cipher_list(*ctx, CIPHER_LIST))
- goto error;
- if (!client_only) {
- if (cert && !SSL_CTX_use_certificate(*ctx,cert))
- goto error;
- X509_free(cert); /* We just added a reference to cert. */
- cert=NULL;
- if (idcert && !SSL_CTX_add_extra_chain_cert(*ctx,idcert))
- goto error;
- idcert=NULL; /* The context now owns the reference to idcert */
- }
- SSL_CTX_set_session_cache_mode(*ctx, SSL_SESS_CACHE_OFF);
- if (isServer && !client_only) {
- tor_assert(rsa);
- if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1)))
- goto error;
- if (!SSL_CTX_use_PrivateKey(*ctx, pkey))
- goto error;
- EVP_PKEY_free(pkey);
- pkey = NULL;
- if (!SSL_CTX_check_private_key(*ctx))
- goto error;
- }
- dh = crypto_dh_new();
- SSL_CTX_set_tmp_dh(*ctx, _crypto_dh_env_get_dh(dh));
- crypto_dh_free(dh);
- SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER,
- always_accept_verify_cb);
- /* let us realloc bufs that we're writing from */
- SSL_CTX_set_mode(*ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- }
+ SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
+ if (!SSL_CTX_set_cipher_list(result->ctx, CIPHER_LIST))
+ goto error;
+ if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
+ goto error;
+ X509_free(cert); /* We just added a reference to cert. */
+ cert=NULL;
+ if (idcert && !SSL_CTX_add_extra_chain_cert(result->ctx,idcert))
+ goto error;
+ idcert=NULL; /* The context now owns the reference to idcert */
+ SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
+ tor_assert(rsa);
+ if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1)))
+ goto error;
+ if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
+ goto error;
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ if (!SSL_CTX_check_private_key(result->ctx))
+ goto error;
+ dh = crypto_dh_new();
+ SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_env_get_dh(dh));
+ crypto_dh_free(dh);
+ SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
+ always_accept_verify_cb);
+ /* let us realloc bufs that we're writing from */
+ SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
/* Free the old context if one exists. */
if (global_tls_context) {
/* This is safe even if there are open connections: OpenSSL does
* reference counting with SSL and SSL_CTX objects. */
SSL_CTX_free(global_tls_context->ctx);
- SSL_CTX_free(global_tls_context->client_only_ctx);
tor_free(global_tls_context);
}
global_tls_context = result;
@@ -420,8 +399,6 @@ tor_tls_context_new(crypto_pk_env_t *identity,
crypto_dh_free(dh);
if (result && result->ctx)
SSL_CTX_free(result->ctx);
- if (result && result->client_only_ctx)
- SSL_CTX_free(result->client_only_ctx);
if (result)
tor_free(result);
if (cert)
@@ -435,20 +412,29 @@ tor_tls_context_new(crypto_pk_env_t *identity,
* determine whether it is functioning as a server.
*/
tor_tls_t *
-tor_tls_new(int sock, int isServer, int use_no_cert)
+tor_tls_new(int sock, int isServer)
{
+ BIO *bio = NULL;
tor_tls_t *result = tor_malloc(sizeof(tor_tls_t));
- SSL_CTX *ctx;
+
tor_assert(global_tls_context); /* make sure somebody made it first */
- ctx = use_no_cert ? global_tls_context->client_only_ctx
- : global_tls_context->ctx;
- if (!(result->ssl = SSL_new(ctx))) {
+ if (!(result->ssl = SSL_new(global_tls_context->ctx))) {
tls_log_errors(LOG_WARN, "generating TLS context");
tor_free(result);
return NULL;
}
result->socket = sock;
- SSL_set_fd(result->ssl, sock);
+#ifdef USE_BSOCKETS
+ bio = BIO_new_bsocket(sock, BIO_NOCLOSE);
+#else
+ bio = BIO_new_socket(sock, BIO_NOCLOSE);
+#endif
+ if (! bio) {
+ tls_log_errors(LOG_WARN, "opening BIO");
+ tor_free(result);
+ return NULL;
+ }
+ SSL_set_bio(result->ssl, bio, bio);
result->state = TOR_TLS_ST_HANDSHAKE;
result->isServer = isServer;
result->wantwrite_n = 0;
@@ -557,7 +543,8 @@ tor_tls_handshake(tor_tls_t *tls)
}
r = tor_tls_get_error(tls,r,0, "handshaking", LOG_INFO);
if (ERR_peek_error() != 0) {
- tls_log_errors(LOG_WARN, "handshaking");
+ tls_log_errors(tls->isServer ? LOG_INFO : LOG_WARN,
+ "handshaking");
return TOR_TLS_ERROR;
}
if (r == TOR_TLS_DONE) {
@@ -717,7 +704,7 @@ log_cert_lifetime(X509 *cert, const char *problem)
BIO_get_mem_ptr(bio, &buf);
s1 = tor_strndup(buf->data, buf->length);
- BIO_reset(bio);
+ (void)BIO_reset(bio);
if (!(ASN1_TIME_print(bio, X509_get_notAfter(cert)))) {
tls_log_errors(LOG_WARN, "printing certificate lifetime");
goto end;
@@ -882,7 +869,7 @@ tor_tls_get_n_bytes_written(tor_tls_t *tls)
}
/** Implement check_no_tls_errors: If there are any pending OpenSSL
- * errors, log an error message and assert(0). */
+ * errors, log an error message. */
void
_check_no_tls_errors(const char *fname, int line)
{
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 82a64cb97a..2569abf79c 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -26,9 +26,9 @@ typedef struct tor_tls_t tor_tls_t;
#define TOR_TLS_DONE 0
void tor_tls_free_all(void);
-int tor_tls_context_new(crypto_pk_env_t *rsa, int isServer,
+int tor_tls_context_new(crypto_pk_env_t *rsa,
const char *nickname, unsigned int key_lifetime);
-tor_tls_t *tor_tls_new(int sock, int is_server, int use_no_cert);
+tor_tls_t *tor_tls_new(int sock, int is_server);
int tor_tls_is_server(tor_tls_t *tls);
void tor_tls_free(tor_tls_t *tls);
int tor_tls_peer_has_cert(tor_tls_t *tls);
diff --git a/src/common/util.c b/src/common/util.c
index f82cfc1263..99ac776dcb 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -46,26 +46,6 @@ const char util_c_id[] = "$Id$";
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_SYS_LIMITS_H
-#include <sys/limits.h>
-#endif
-#ifdef HAVE_MACHINE_LIMITS_H
-#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
- /* FreeBSD has a bug where it complains that this file is obsolete,
- and I should migrate to using sys/limits. It complains even when
- I include both.
- __FreeBSD_kernel__ is defined by Debian GNU/kFreeBSD which
- does the same thing (but doesn't defined __FreeBSD__).
- */
-#include <machine/limits.h>
-#endif
-#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h> /* Must be included before sys/stat.h for Ultrix */
-#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
@@ -126,19 +106,21 @@ _tor_malloc(size_t size DMALLOC_PARAMS)
{
void *result;
+#ifndef MALLOC_ZERO_WORKS
/* Some libcs don't do the right thing on size==0. Override them. */
if (size==0) {
size=1;
}
+#endif
result = dmalloc_malloc(file, line, size, DMALLOC_FUNC_MALLOC, 0, 0);
- if (!result) {
+ if (PREDICT(result == NULL, 0)) {
log_err(LD_MM,"Out of memory. Dying.");
- /* XXX if these functions die within a worker process, they won't
- * call spawn_exit */
+ /* If these functions die within a worker process, they won't call
+ * spawn_exit, but that's ok, since the parent will run out of memory soon
+ * anyway. */
exit(1);
}
-// memset(result,'X',size); /* deadbeef to encourage bugs */
return result;
}
@@ -164,7 +146,7 @@ _tor_realloc(void *ptr, size_t size DMALLOC_PARAMS)
void *result;
result = dmalloc_realloc(file, line, ptr, size, DMALLOC_FUNC_REALLOC, 0);
- if (!result) {
+ if (PREDICT(result == NULL, 0)) {
log_err(LD_MM,"Out of memory. Dying.");
exit(1);
}
@@ -182,7 +164,7 @@ _tor_strdup(const char *s DMALLOC_PARAMS)
tor_assert(s);
dup = dmalloc_strdup(file, line, s, 0);
- if (!dup) {
+ if (PREDICT(dup == NULL, 0)) {
log_err(LD_MM,"Out of memory. Dying.");
exit(1);
}
@@ -210,6 +192,26 @@ _tor_strndup(const char *s, size_t n DMALLOC_PARAMS)
return dup;
}
+/** Allocate a chunk of <b>len</b> bytes, with the same contents starting at
+ * <b>mem</b>. */
+void *
+_tor_memdup(const void *mem, size_t len DMALLOC_PARAMS)
+{
+ char *dup;
+ tor_assert(mem);
+ dup = _tor_malloc(len DMALLOC_FN_ARGS);
+ memcpy(dup, mem, len);
+ return dup;
+}
+
+/** Helper for places that need to take a function pointer to the right
+ * spelling of "free()". */
+void
+_tor_free(void *mem)
+{
+ tor_free(mem);
+}
+
/* =====
* String manipulation
* ===== */
@@ -345,6 +347,19 @@ tor_strisprint(const char *s)
return 1;
}
+/** Return 1 if no character in <b>s</b> is uppercase, else return 0.
+ */
+int
+tor_strisnonupper(const char *s)
+{
+ while (*s) {
+ if (TOR_ISUPPER(*s))
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
/* Compares the first strlen(s2) characters of s1 with s2. Returns as for
* strcmp.
*/
@@ -399,17 +414,23 @@ eat_whitespace(const char *s)
{
tor_assert(s);
- while (TOR_ISSPACE(*s) || *s == '#') {
- while (TOR_ISSPACE(*s))
- s++;
- if (*s == '#') { /* read to a \n or \0 */
+ while (1) {
+ switch (*s) {
+ case '\0':
+ default:
+ return s;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ ++s;
+ break;
+ case '#':
+ ++s;
while (*s && *s != '\n')
- s++;
- if (!*s)
- return s;
+ ++s;
}
}
- return s;
}
/** Return a pointer to the first char of s that is not a space or a tab,
@@ -429,20 +450,47 @@ const char *
find_whitespace(const char *s)
{
/* tor_assert(s); */
+ while (1) {
+ switch (*s)
+ {
+ case '\0':
+ case '#':
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ return s;
+ default:
+ ++s;
+ }
+ }
+}
- while (*s && !TOR_ISSPACE(*s) && *s != '#')
- s++;
+/** Return true iff the 'len' bytes at 'mem' are all zero. */
+int
+tor_mem_is_zero(const char *mem, size_t len)
+{
+ static const char ZERO[] = {
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ };
+ while (len >= sizeof(ZERO)) {
+ if (memcmp(mem, ZERO, sizeof(ZERO)))
+ return 0;
+ len -= sizeof(ZERO);
+ mem += sizeof(ZERO);
+ }
+ /* Deal with leftover bytes. */
+ if (len)
+ return ! memcmp(mem, ZERO, len);
- return s;
+ return 1;
}
/** Return true iff the DIGEST_LEN bytes in digest are all zero. */
int
tor_digest_is_zero(const char *digest)
{
- static char ZERO_DIGEST[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
-
- return !memcmp(digest, ZERO_DIGEST, DIGEST_LEN);
+ return tor_mem_is_zero(digest, DIGEST_LEN);
}
#define CHECK_STRTOX_RESULT() \
@@ -913,7 +961,7 @@ write_all(int fd, const char *buf, size_t count, int isSocket)
while (written != count) {
if (isSocket)
- result = send(fd, buf+written, count-written, 0);
+ result = tor_socket_send(fd, buf+written, count-written, 0);
else
result = write(fd, buf+written, count-written);
if (result<0)
@@ -939,7 +987,7 @@ read_all(int fd, char *buf, size_t count, int isSocket)
while (numread != count) {
if (isSocket)
- result = recv(fd, buf+numread, count-numread, 0);
+ result = tor_socket_recv(fd, buf+numread, count-numread, 0);
else
result = read(fd, buf+numread, count-numread);
if (result<0)
@@ -970,6 +1018,8 @@ clean_name_for_stat(char *name)
return;
name[len-1]='\0';
}
+#else
+ (void)name;
#endif
}
@@ -1492,8 +1542,8 @@ is_local_IP(uint32_t ip)
* Return 0 on success, -1 on failure.
*/
int
-parse_addr_port(const char *addrport, char **address, uint32_t *addr,
- uint16_t *port_out)
+parse_addr_port(int severity, const char *addrport, char **address,
+ uint32_t *addr, uint16_t *port_out)
{
const char *colon;
char *_address = NULL;
@@ -1507,14 +1557,14 @@ parse_addr_port(const char *addrport, char **address, uint32_t *addr,
_address = tor_strndup(addrport, colon-addrport);
_port = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL);
if (!_port) {
- log_warn(LD_GENERAL, "Port %s out of range", escaped(colon+1));
+ log_fn(severity, LD_GENERAL, "Port %s out of range", escaped(colon+1));
ok = 0;
}
if (!port_out) {
char *esc_addrport = esc_for_log(addrport);
- log_warn(LD_GENERAL,
- "Port %s given on %s when not required",
- escaped(colon+1), esc_addrport);
+ log_fn(severity, LD_GENERAL,
+ "Port %s given on %s when not required",
+ escaped(colon+1), esc_addrport);
tor_free(esc_addrport);
ok = 0;
}
@@ -1526,7 +1576,7 @@ parse_addr_port(const char *addrport, char **address, uint32_t *addr,
if (addr) {
/* There's an addr pointer, so we need to resolve the hostname. */
if (tor_lookup_hostname(_address,addr)) {
- log_warn(LD_NET, "Couldn't look up %s", escaped(_address));
+ log_fn(severity, LD_NET, "Couldn't look up %s", escaped(_address));
ok = 0;
*addr = 0;
}
@@ -1717,8 +1767,9 @@ tor_dup_addr(uint32_t addr)
return tor_strdup(buf);
}
-/* Return true iff <b>name</b> looks like it might be a hostname or IP
- * address of some kind. */
+/* Return true iff <b>name</b> looks like it might be a hostname,
+ * nickname, key, or IP address of some kind, suitable for the
+ * controller's "mapaddress" command. */
int
is_plausible_address(const char *name)
{
@@ -1744,7 +1795,7 @@ is_plausible_address(const char *name)
* failure.
*/
int
-get_interface_address(uint32_t *addr)
+get_interface_address(int severity, uint32_t *addr)
{
int sock=-1, r=-1;
struct sockaddr_in target_addr, my_addr;
@@ -1756,7 +1807,8 @@ get_interface_address(uint32_t *addr)
sock = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
if (sock < 0) {
int e = tor_socket_errno(-1);
- log_warn(LD_NET, "unable to create socket: %s", tor_socket_strerror(e));
+ log_fn(severity, LD_NET, "unable to create socket: %s",
+ tor_socket_strerror(e));
goto err;
}
@@ -1770,14 +1822,15 @@ get_interface_address(uint32_t *addr)
if (connect(sock,(struct sockaddr *)&target_addr,sizeof(target_addr))<0) {
int e = tor_socket_errno(sock);
- log_warn(LD_NET, "connnect() failed: %s", tor_socket_strerror(e));
+ log_fn(severity, LD_NET, "connect() failed: %s", tor_socket_strerror(e));
goto err;
}
/* XXXX Can this be right on IPv6 clients? */
if (getsockname(sock, (struct sockaddr*)&my_addr, &my_addr_len)) {
int e = tor_socket_errno(sock);
- log_warn(LD_NET, "getsockname() failed: %s", tor_socket_strerror(e));
+ log_fn(severity, LD_NET, "getsockname() failed: %s",
+ tor_socket_strerror(e));
goto err;
}
diff --git a/src/common/util.h b/src/common/util.h
index cdcd64f6b4..22f4ea3b99 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -28,7 +28,7 @@
* calling assert() normally.
*/
#ifdef NDEBUG
-/* Nobody should ever want to build with NDEBUG set. 99% of your asserts will
+/* Nobody should ever want to build with NDEBUG set. 99% of our asserts will
* be outside the critical path anyway, so it's silly to disable bugchecking
* throughout the entire program just because a few asserts are slowing you
* down. Profile, optimize the critical path, and keep debugging on.
@@ -38,12 +38,17 @@
*/
#error "Sorry; we don't support building with NDEBUG."
#else
+#ifdef __GNUC__
+#define PREDICT_FALSE(x) PREDICT((x) == ((typeof(x)) 0), 0)
+#else
+#define PREDICT_FALSE(x) !(x)
+#endif
#define tor_assert(expr) do { \
- if (!(expr)) { \
+ if (PREDICT_FALSE(expr)) { \
log(LOG_ERR, LD_BUG, "%s:%d: %s: Assertion %s failed; aborting.", \
- _SHORT_FILE_, __LINE__, __func__, #expr); \
+ _SHORT_FILE_, __LINE__, __func__, #expr); \
fprintf(stderr,"%s:%d %s: Assertion %s failed; aborting.\n", \
- _SHORT_FILE_, __LINE__, __func__, #expr); \
+ _SHORT_FILE_, __LINE__, __func__, #expr); \
abort(); \
} } while (0)
#endif
@@ -62,22 +67,27 @@
#define tor_fragile_assert()
/* Memory management */
-void *_tor_malloc(size_t size DMALLOC_PARAMS);
-void *_tor_malloc_zero(size_t size DMALLOC_PARAMS);
+void *_tor_malloc(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
+void *_tor_malloc_zero(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
void *_tor_realloc(void *ptr, size_t size DMALLOC_PARAMS);
-char *_tor_strdup(const char *s DMALLOC_PARAMS);
-char *_tor_strndup(const char *s, size_t n DMALLOC_PARAMS);
+char *_tor_strdup(const char *s DMALLOC_PARAMS) ATTR_MALLOC ATTR_NONNULL((1));
+char *_tor_strndup(const char *s, size_t n DMALLOC_PARAMS)
+ ATTR_MALLOC ATTR_NONNULL((1));
+void *_tor_memdup(const void *mem, size_t len DMALLOC_PARAMS)
+ ATTR_MALLOC ATTR_NONNULL((1));
+void _tor_free(void *mem);
#ifdef USE_DMALLOC
extern int dmalloc_free(const char *file, const int line, void *pnt,
const int func_id);
#define tor_free(p) do { \
- if (p) { \
+ if (PREDICT((p)!=NULL, 1) { \
dmalloc_free(_SHORT_FILE_, __LINE__, (p), 0); \
(p)=NULL; \
} \
} while (0)
#else
-#define tor_free(p) do { if (p) {free(p); (p)=NULL;} } while (0)
+#define tor_free(p) do { if (PREDICT((p)!=NULL,1)) { free(p); (p)=NULL;} } \
+ while (0)
#endif
#define tor_malloc(size) _tor_malloc(size DMALLOC_ARGS)
@@ -85,17 +95,29 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
#define tor_realloc(ptr, size) _tor_realloc(ptr, size DMALLOC_ARGS)
#define tor_strdup(s) _tor_strdup(s DMALLOC_ARGS)
#define tor_strndup(s, n) _tor_strndup(s, n DMALLOC_ARGS)
+#define tor_memdup(s, n) _tor_memdup(s, n DMALLOC_ARGS)
+
+/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
+#if defined(__GNUC__) && __GNUC__ > 3
+#define STRUCT_OFFSET(tp, member) __builtin_offsetof(tp, member)
+#else
+ #define STRUCT_OFFSET(tp, member) \
+ ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
+#endif
/* String manipulation */
#define HEX_CHARACTERS "0123456789ABCDEFabcdef"
-void tor_strlower(char *s);
-void tor_strupper(char *s);
-int tor_strisprint(const char *s);
-int strcmpstart(const char *s1, const char *s2);
-int strcasecmpstart(const char *s1, const char *s2);
-int strcmpend(const char *s1, const char *s2);
-int strcasecmpend(const char *s1, const char *s2);
-int tor_strstrip(char *s, const char *strip);
+void tor_strlower(char *s) ATTR_NONNULL((1));
+void tor_strupper(char *s) ATTR_NONNULL((1));
+int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1));
+int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1));
+int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
+int strcasecmpstart(const char *s1, const char *s2)
+ ATTR_PURE ATTR_NONNULL((1,2));
+int strcmpend(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
+int strcasecmpend(const char *s1, const char *s2)
+ ATTR_PURE ATTR_NONNULL((1,2));
+int tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2));
typedef enum {
ALWAYS_TERMINATE, NEVER_TERMINATE, TERMINATE_IF_EVEN
} part_finish_rule_t;
@@ -108,12 +130,13 @@ unsigned long tor_parse_ulong(const char *s, int base, unsigned long min,
unsigned long max, int *ok, char **next);
uint64_t tor_parse_uint64(const char *s, int base, uint64_t min,
uint64_t max, int *ok, char **next);
-const char *hex_str(const char *from, size_t fromlen);
-const char *eat_whitespace(const char *s);
-const char *eat_whitespace_no_nl(const char *s);
-const char *find_whitespace(const char *s);
-int tor_digest_is_zero(const char *digest);
-char *esc_for_log(const char *string);
+const char *hex_str(const char *from, size_t fromlen) ATTR_NONNULL((1));
+const char *eat_whitespace(const char *s) ATTR_PURE;
+const char *eat_whitespace_no_nl(const char *s) ATTR_PURE;
+const char *find_whitespace(const char *s) ATTR_PURE;
+int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE;
+int tor_digest_is_zero(const char *digest) ATTR_PURE;
+char *esc_for_log(const char *string) ATTR_MALLOC;
const char *escaped(const char *string);
void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
@@ -157,17 +180,17 @@ int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
int append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);
-char *read_file_to_str(const char *filename, int bin);
+char *read_file_to_str(const char *filename, int bin) ATTR_MALLOC;
char *parse_line_from_str(char *line, char **key_out, char **value_out);
char *expand_filename(const char *filename);
struct smartlist_t *tor_listdir(const char *dirname);
-int path_is_relative(const char *filename);
+int path_is_relative(const char *filename) ATTR_PURE;
/* Net helpers */
-int is_internal_IP(uint32_t ip, int for_listening);
-int is_local_IP(uint32_t ip);
-int parse_addr_port(const char *addrport, char **address, uint32_t *addr,
- uint16_t *port);
+int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE;
+int is_local_IP(uint32_t ip) ATTR_PURE;
+int parse_addr_port(int severity, const char *addrport, char **address,
+ uint32_t *addr, uint16_t *port_out);
int parse_port_range(const char *port, uint16_t *port_min_out,
uint16_t *port_max_out);
int parse_addr_and_port_range(const char *s, uint32_t *addr_out,
@@ -176,9 +199,9 @@ int parse_addr_and_port_range(const char *s, uint32_t *addr_out,
int addr_mask_get_bits(uint32_t mask);
#define INET_NTOA_BUF_LEN 16
int tor_inet_ntoa(struct in_addr *in, char *buf, size_t buf_len);
-char *tor_dup_addr(uint32_t addr);
+char *tor_dup_addr(uint32_t addr) ATTR_MALLOC;
int is_plausible_address(const char *name);
-int get_interface_address(uint32_t *addr);
+int get_interface_address(int severity, uint32_t *addr);
/* Process helpers */
void start_daemon(void);
diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in
index c632d02fe5..7def88ed8a 100644
--- a/src/config/torrc.sample.in
+++ b/src/config/torrc.sample.in
@@ -1,5 +1,5 @@
## Configuration file for a typical Tor user
-## Last updated 9 February 2006 for Tor 0.1.1.13-alpha.
+## Last updated 31 July 2006 for Tor 0.1.2.1-alpha.
## (May or may not work for older or newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
@@ -23,7 +23,7 @@
## server, and not make any local application connections yourself.
SocksPort 9050 # what port to open for local application connections
SocksListenAddress 127.0.0.1 # accept connections only from localhost
-#SocksListenAddress 192.168.0.1:9100 # listen on a chosen IP/port too
+#SocksListenAddress 192.168.0.1:9100 # listen on this IP:port also
## Entry policies to allow/deny SOCKS requests based on IP address.
## First entry that matches wins. If no SocksPolicy is set, we accept
@@ -62,9 +62,12 @@ SocksListenAddress 127.0.0.1 # accept connections only from localhost
############### This section is just for location-hidden services ###
-## Look in .../hidden_service/hostname for the address to tell people.
-## HiddenServicePort x y:z says to redirect a port x request from the
-## client to y:z.
+## Once you have configured a hidden service, you can look at the
+## contents of the file ".../hidden_service/hostname" for the address
+## to tell people.
+##
+## HiddenServicePort x y:z says to redirect requests on port x to the
+## address y:z.
#HiddenServiceDir @LOCALSTATEDIR@/lib/tor/hidden_service/
#HiddenServicePort 80 127.0.0.1:80
@@ -77,29 +80,33 @@ SocksListenAddress 127.0.0.1 # accept connections only from localhost
################ This section is just for servers #####################
-## NOTE: If you enable these, you should consider mailing your identity
-## key fingerprint to the tor-ops, so we can add you to the list of
-## servers that clients will trust. See
-## http://tor.eff.org/cvs/tor/doc/tor-doc-server.html for details.
+## NOTE: If you enable these, you should consider mailing the contents
+## of the "fingerprint" file to the tor-ops, so nobody else can pick
+## your nickname and use a different key. See
+## http://tor.eff.org/docs/tor-doc-server.html for details.
-## Required: A unique handle for this server
+## Required: A unique handle for your server.
#Nickname ididnteditheconfig
-## The IP or fqdn for this server. Leave commented out and Tor will guess.
+## The IP or FQDN for your server. Leave commented out and Tor will guess.
#Address noname.example.com
-## Contact info that will be published in the directory, so we can
-## contact you if you need to upgrade or if something goes wrong.
-## This is optional but recommended.
+## Define these to limit your bandwidth usage. Note that BandwidthRate
+## must be at least 20 KB.
+#BandwidthRate 100 KB # Throttle traffic to 100KB/s (800Kbps)
+#BandwidthBurst 200 KB # But allow bursts up to 200KB/s (1600Kbps)
+
+## Contact info to be published in the directory, so we can contact you
+## if your server is misconfigured or something else goes wrong.
#ContactInfo Random Person <nobody AT example dot com>
## You might also include your PGP or GPG fingerprint if you have one:
#ContactInfo 1234D/FFFFFFFF Random Person <nobody AT example dot com>
-## Required: what port to advertise for tor connections
+## Required: what port to advertise for Tor connections.
#ORPort 9001
## If you want to listen on a port other than the one advertised
-## in ORPort (e.g. to advertise 443 but bind to 9090), uncomment
-## the line below. You'll need to do ipchains or other port forwarding
+## in ORPort (e.g. to advertise 443 but bind to 9090), uncomment the
+## line below too. You'll need to do ipchains or other port forwarding
## yourself to make this work.
#ORListenAddress 0.0.0.0:9090
@@ -108,15 +115,15 @@ SocksListenAddress 127.0.0.1 # accept connections only from localhost
## http://wiki.noreply.org/noreply/TheOnionRouter/TorFAQ#LimitBandwidth
#DirPort 9030 # what port to advertise for directory connections
## If you want to listen on a port other than the one advertised
-## in DirPort (e.g. to advertise 80 but bind 9091), uncomment the line
-## below. You'll need to do ipchains or other port forwarding yourself
+## in DirPort (e.g. to advertise 80 but bind to 9091), uncomment the line
+## below too. You'll need to do ipchains or other port forwarding yourself
## to make this work.
#DirListenAddress 0.0.0.0:9091
## Uncomment this if you run more than one Tor server, and add the
## nickname of each Tor server you control, even if they're on different
-## networks. We declare it here so clients can avoid using more than
-## one of your servers in a given circuit.
+## networks. You declare it here so Tor clients can avoid using more than
+## one of your servers in a single circuit.
#MyFamily nickname1,nickname2,...
## A comma-separated list of exit policies. They're considered first
@@ -135,5 +142,5 @@ SocksListenAddress 127.0.0.1 # accept connections only from localhost
##
#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more
#ExitPolicy accept *:119 # accept nntp as well as default exit policy
-#ExitPolicy reject *:* # middleman only -- no exits allowed
+#ExitPolicy reject *:* # no exits allowed
diff --git a/src/or/Makefile.am b/src/or/Makefile.am
index 6a77adffaa..91ec1c057c 100644
--- a/src/or/Makefile.am
+++ b/src/or/Makefile.am
@@ -4,12 +4,19 @@ noinst_PROGRAMS = test
bin_PROGRAMS = tor
+if EVENTDNS
+EVDNSSRC = eventdns.c
+else
+EVDNSSRC =
+endif
+
tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \
circuituse.c command.c config.c \
connection.c connection_edge.c connection_or.c control.c \
cpuworker.c directory.c dirserv.c dns.c hibernate.c main.c \
onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \
rendservice.c rephist.c router.c routerlist.c routerparse.c \
+ $(EVDNSSRC) \
tor_main.c
tor_LDADD = ../common/libor.a ../common/libor-crypto.a -lz -lssl -lcrypto
@@ -20,9 +27,10 @@ test_SOURCES = buffers.c circuitbuild.c circuitlist.c \
cpuworker.c directory.c dirserv.c dns.c hibernate.c main.c \
onion.c policies.c relay.c rendcommon.c rendclient.c rendmid.c \
rendservice.c rephist.c router.c routerlist.c routerparse.c \
+ $(EVDNSSRC) \
test.c
test_LDADD = ../common/libor.a ../common/libor-crypto.a -lz -lssl -lcrypto
-noinst_HEADERS = or.h
+noinst_HEADERS = or.h eventdns.h eventdns_tor.h eventdns.c
diff --git a/src/or/buffers.c b/src/or/buffers.c
index f5dd19135b..290a81c8b2 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -58,10 +58,15 @@ struct buf_t {
char *cur; /**< The first byte used for storing data in the buffer. */
size_t highwater; /**< Largest observed datalen since last buf_shrink */
size_t len; /**< Maximum amount of data that <b>mem</b> can hold. */
+ size_t memsize; /**< How many bytes did we actually allocate? Can be less
+ * than 'len' if we shortened 'len' by a few bytes to make
+ * zlib wrap around more easily. */
size_t datalen; /**< Number of bytes currently in <b>mem</b>. */
};
+/** How many bytes, total, are used in all buffers? */
uint64_t buf_total_used = 0;
+/** How many bytes, total, are allocated in all buffers? */
uint64_t buf_total_alloc = 0;
/** Size, in bytes, for newly allocated buffers. Should be a power of 2. */
@@ -88,13 +93,14 @@ buf_normalize(buf_t *buf)
char *newmem, *oldmem;
size_t sz = (buf->mem+buf->len)-buf->cur;
log_warn(LD_BUG, "Unexpected non-normalized buffer.");
- newmem = GUARDED_MEM(tor_malloc(ALLOC_LEN(buf->len)));
- SET_GUARDS(newmem, buf->len);
+ newmem = GUARDED_MEM(tor_malloc(ALLOC_LEN(buf->memsize)));
+ SET_GUARDS(newmem, buf->memsize);
memcpy(newmem, buf->cur, sz);
memcpy(newmem+sz, buf->mem, buf->datalen-sz);
oldmem = RAW_MEM(buf->mem);
tor_free(oldmem); /* Can't use tor_free directly. */
buf->mem = buf->cur = newmem;
+ buf->len = buf->memsize;
check();
}
}
@@ -229,7 +235,7 @@ buf_resize(buf_t *buf, size_t new_capacity)
buf->len-offset);
buf->cur += new_capacity-buf->len;
}
- buf->len = new_capacity;
+ buf->memsize = buf->len = new_capacity;
#ifdef CHECK_AFTER_RESIZE
assert_buf_ok(buf);
@@ -249,15 +255,18 @@ buf_resize(buf_t *buf, size_t new_capacity)
static INLINE int
buf_ensure_capacity(buf_t *buf, size_t capacity)
{
- size_t new_len;
+ size_t new_len, min_len;
if (buf->len >= capacity) /* Don't grow if we're already big enough. */
return 0;
if (capacity > MAX_BUF_SIZE) /* Don't grow past the maximum. */
return -1;
- /* Find the smallest new_len equal to (2**X)*len for some X; such that
- * new_len is at least capacity.
+ /* Find the smallest new_len equal to (2**X) for some X; such that
+ * new_len is at least capacity, and at least 2*buf->len.
*/
- new_len = buf->len*2;
+ min_len = buf->len*2;
+ new_len = 16;
+ while (new_len < min_len)
+ new_len *= 2;
while (new_len < capacity)
new_len *= 2;
/* Resize the buffer. */
@@ -325,7 +334,7 @@ buf_new_with_capacity(size_t size)
buf->magic = BUFFER_MAGIC;
buf->cur = buf->mem = GUARDED_MEM(tor_malloc(ALLOC_LEN(size)));
SET_GUARDS(buf->mem, size);
- buf->len = size;
+ buf->len = buf->memsize = size;
buf_total_alloc += size;
assert_buf_ok(buf);
@@ -346,6 +355,7 @@ buf_clear(buf_t *buf)
buf_total_used -= buf->datalen;
buf->datalen = 0;
buf->cur = buf->mem;
+ buf->len = buf->memsize;
}
/** Return the number of bytes stored in <b>buf</b> */
@@ -398,7 +408,7 @@ read_to_buf_impl(int s, size_t at_most, buf_t *buf,
int read_result;
// log_fn(LOG_DEBUG,"reading at most %d bytes.",at_most);
- read_result = recv(s, pos, at_most, 0);
+ read_result = tor_socket_recv(s, pos, at_most, 0);
if (read_result < 0) {
int e = tor_socket_errno(s);
if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
@@ -572,7 +582,7 @@ flush_buf_impl(int s, buf_t *buf, size_t sz, size_t *buf_flushlen)
{
int write_result;
- write_result = send(s, buf->cur, sz, 0);
+ write_result = tor_socket_send(s, buf->cur, sz, 0);
if (write_result < 0) {
int e = tor_socket_errno(s);
if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
@@ -872,14 +882,14 @@ fetch_from_buf_http(buf_t *buf,
if (headers_out) {
*headers_out = tor_malloc(headerlen+1);
memcpy(*headers_out,buf->cur,headerlen);
- (*headers_out)[headerlen] = 0; /* null terminate it */
+ (*headers_out)[headerlen] = 0; /* nul terminate it */
}
if (body_out) {
tor_assert(body_used);
*body_used = bodylen;
*body_out = tor_malloc(bodylen+1);
memcpy(*body_out,buf->cur+headerlen,bodylen);
- (*body_out)[bodylen] = 0; /* null terminate it */
+ (*body_out)[bodylen] = 0; /* nul terminate it */
}
buf_remove_from_front(buf, headerlen+bodylen);
return 1;
@@ -947,7 +957,9 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
req->reply[1] = '\xFF'; /* reject all methods */
return -1;
}
- buf_remove_from_front(buf,2+nummethods); /* remove packet from buf */
+ /* remove packet from buf. also remove any other extraneous
+ * bytes, to support broken socks clients. */
+ buf_clear(buf);
req->replylen = 2; /* 2 bytes of response */
req->reply[0] = 5; /* socks5 reply */
@@ -962,8 +974,9 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return 0; /* not yet */
req->command = (unsigned char) *(buf->cur+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
- req->command != SOCKS_COMMAND_RESOLVE) {
- /* not a connect or resolve? we don't support it. */
+ req->command != SOCKS_COMMAND_RESOLVE &&
+ req->command != SOCKS_COMMAND_RESOLVE_PTR) {
+ /* not a connect or resolve or a resolve_ptr? we don't support it. */
log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
req->command);
return -1;
@@ -987,7 +1000,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
strlcpy(req->address,tmpbuf,sizeof(req->address));
req->port = ntohs(*(uint16_t*)(buf->cur+8));
buf_remove_from_front(buf, 10);
- if (!address_is_in_virtual_range(req->address) &&
+ if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
+ !addressmap_have_mapping(req->address) &&
!have_warned_about_unsafe_socks) {
log_warn(LD_APP,
"Your application (using socks5 on port %d) is giving "
@@ -1013,6 +1027,11 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
"%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
return -1;
}
+ if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
+ log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
+ "hostname type. Rejecting.");
+ return -1;
+ }
memcpy(req->address,buf->cur+5,len);
req->address[len] = 0;
req->port = ntohs(get_uint16(buf->cur+5+len));
@@ -1047,7 +1066,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
req->command = (unsigned char) *(buf->cur+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
req->command != SOCKS_COMMAND_RESOLVE) {
- /* not a connect or resolve? we don't support it. */
+ /* not a connect or resolve? we don't support it. (No resolve_ptr with
+ * socks4.) */
log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
req->command);
return -1;
@@ -1083,7 +1103,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
startaddr = NULL;
if (socks4_prot != socks4a &&
- !address_is_in_virtual_range(tmpbuf) &&
+ !addressmap_have_mapping(tmpbuf) &&
!have_warned_about_unsafe_socks) {
log_warn(LD_APP,
"Your application (using socks4 on port %d) is giving Tor "
@@ -1284,6 +1304,62 @@ fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len)
return 1;
}
+/** Compress on uncompress the <b>data_len</b> bytes in <b>data</b> using the
+ * zlib state <b>state</b>, appending the result to <b>buf</b>. If
+ * <b>done</b> is true, flush the data in the state and finish the
+ * compression/uncompression. Return -1 on failure, 0 on success. */
+int
+write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state,
+ const char *data, size_t data_len,
+ int done)
+{
+ char *next;
+ size_t old_avail, avail;
+ int over = 0;
+ do {
+ buf_ensure_capacity(buf, buf->datalen + 1024);
+ next = _buf_end(buf);
+ if (next < buf->cur)
+ old_avail = avail = buf->cur - next;
+ else
+ old_avail = avail = (buf->mem + buf->len) - next;
+ switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) {
+ case TOR_ZLIB_DONE:
+ over = 1;
+ break;
+ case TOR_ZLIB_ERR:
+ return -1;
+ case TOR_ZLIB_OK:
+ if (data_len == 0)
+ over = 1;
+ break;
+ case TOR_ZLIB_BUF_FULL:
+ if (avail && buf->len >= 1024 + buf->datalen) {
+ /* Zlib says we need more room (ZLIB_BUF_FULL), and we're not about
+ * to wrap around (avail != 0), and resizing won't actually make us
+ * un-full: we're at the end of the buffer, and zlib refuses to
+ * append more here, but there's a pile of free space at the start
+ * of the buffer (about 1K). So chop a few characters off the
+ * end of the buffer. This feels silly; anybody got a better hack?
+ *
+ * (We don't just want to expand the buffer nevertheless. Consider a
+ * 1/3 full buffer with a single byte free at the end. zlib will
+ * often refuse to append to that, and so we want to use the
+ * beginning, not double the buffer to be just 1/6 full.)
+ */
+ tor_assert(next >= buf->cur);
+ buf->len -= avail;
+ }
+ break;
+ }
+ buf->datalen += old_avail - avail;
+ if (buf->datalen > buf->highwater)
+ buf->highwater = buf->datalen;
+ buf_total_used += old_avail - avail;
+ } while (!over);
+ return 0;
+}
+
/** Log an error and exit if <b>buf</b> is corrupted.
*/
void
@@ -1298,7 +1374,7 @@ assert_buf_ok(buf_t *buf)
{
uint32_t u32 = get_uint32(buf->mem - 4);
tor_assert(u32 == START_MAGIC);
- u32 = get_uint32(buf->mem + buf->len);
+ u32 = get_uint32(buf->mem + buf->memsize);
tor_assert(u32 == END_MAGIC);
}
#endif
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 6a9ed3b16b..d23f21b704 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -43,7 +43,7 @@ static int entry_guards_dirty = 0;
static int circuit_deliver_create_cell(circuit_t *circ,
uint8_t cell_type, char *payload);
-static int onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit);
+static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
cpath_build_state_t *state);
@@ -54,21 +54,19 @@ static routerinfo_t *choose_random_entry(cpath_build_state_t *state);
static void entry_guards_changed(void);
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
- * and with the high bit specified by circ_id_type (see
- * decide_circ_id_type()), until we get a circ_id that is not in use
- * by any other circuit on that conn.
+ * and with the high bit specified by conn-\>circ_id_type, until we get
+ * a circ_id that is not in use by any other circuit on that conn.
*
* Return it, or 0 if can't get a unique circ_id.
*/
static uint16_t
-get_unique_circ_id_by_conn(connection_t *conn)
+get_unique_circ_id_by_conn(or_connection_t *conn)
{
uint16_t test_circ_id;
uint16_t attempts=0;
uint16_t high_bit;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_OR);
high_bit = (conn->circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0;
do {
/* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a
@@ -96,14 +94,13 @@ get_unique_circ_id_by_conn(connection_t *conn)
* a more verbose format using spaces.
*/
char *
-circuit_list_path(circuit_t *circ, int verbose)
+circuit_list_path(origin_circuit_t *circ, int verbose)
{
crypt_path_t *hop;
smartlist_t *elements;
const char *states[] = {"closed", "waiting for keys", "open"};
char buf[128];
char *s;
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
elements = smartlist_create();
@@ -113,8 +110,8 @@ circuit_list_path(circuit_t *circ, int verbose)
circ->build_state->is_internal ? "internal" : "exit",
circ->build_state->need_uptime ? " (high-uptime)" : "",
circ->build_state->desired_path_len,
- circ->state == CIRCUIT_STATE_OPEN ? "" : ", exit ",
- circ->state == CIRCUIT_STATE_OPEN ? "" :
+ circ->_base.state == CIRCUIT_STATE_OPEN ? "" : ", exit ",
+ circ->_base.state == CIRCUIT_STATE_OPEN ? "" :
(nickname?nickname:"*unnamed*"));
smartlist_add(elements, tor_strdup(buf));
}
@@ -153,7 +150,7 @@ circuit_list_path(circuit_t *circ, int verbose)
* exit point.
*/
void
-circuit_log_path(int severity, unsigned int domain, circuit_t *circ)
+circuit_log_path(int severity, unsigned int domain, origin_circuit_t *circ)
{
char *s = circuit_list_path(circ,1);
log(severity,domain,"%s",s);
@@ -166,7 +163,7 @@ circuit_log_path(int severity, unsigned int domain, circuit_t *circ)
* unable to extend.
*/
void
-circuit_rep_hist_note_result(circuit_t *circ)
+circuit_rep_hist_note_result(origin_circuit_t *circ)
{
crypt_path_t *hop;
char *prev_digest = NULL;
@@ -207,74 +204,14 @@ circuit_rep_hist_note_result(circuit_t *circ)
} while (hop!=circ->cpath);
}
-/** A helper function for circuit_dump_by_conn() below. Log a bunch
- * of information about circuit <b>circ</b>.
- */
-static void
-circuit_dump_details(int severity, circuit_t *circ, int poll_index,
- const char *type, int this_circid, int other_circid)
-{
- log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), "
- "state %d (%s), born %d:",
- poll_index, type, this_circid, other_circid, circ->state,
- circuit_state_to_string(circ->state), (int)circ->timestamp_created);
- if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */
- circuit_log_path(severity, LD_CIRC, circ);
- }
-}
-
-/** Log, at severity <b>severity</b>, information about each circuit
- * that is connected to <b>conn</b>.
- */
-void
-circuit_dump_by_conn(connection_t *conn, int severity)
-{
- circuit_t *circ;
- connection_t *tmpconn;
-
- for (circ=global_circuitlist;circ;circ = circ->next) {
- if (circ->marked_for_close)
- continue;
- if (circ->p_conn == conn)
- circuit_dump_details(severity, circ, conn->poll_index, "App-ward",
- circ->p_circ_id, circ->n_circ_id);
- for (tmpconn=circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) {
- if (tmpconn == conn) {
- circuit_dump_details(severity, circ, conn->poll_index, "App-ward",
- circ->p_circ_id, circ->n_circ_id);
- }
- }
- if (circ->n_conn == conn)
- circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward",
- circ->n_circ_id, circ->p_circ_id);
- for (tmpconn=circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) {
- if (tmpconn == conn) {
- circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward",
- circ->n_circ_id, circ->p_circ_id);
- }
- }
- if (!circ->n_conn && circ->n_addr && circ->n_port &&
- circ->n_addr == conn->addr &&
- circ->n_port == conn->port &&
- !memcmp(conn->identity_digest, circ->n_conn_id_digest, DIGEST_LEN)) {
- circuit_dump_details(severity, circ, conn->poll_index,
- (circ->state == CIRCUIT_STATE_OPEN &&
- !CIRCUIT_IS_ORIGIN(circ)) ?
- "Endpoint" : "Pending",
- circ->n_circ_id, circ->p_circ_id);
- }
- }
-}
-
/** Pick all the entries in our cpath. Stop and return 0 when we're
* happy, or return -1 if an error occurs. */
static int
-onion_populate_cpath(circuit_t *circ)
+onion_populate_cpath(origin_circuit_t *circ)
{
int r;
again:
- r = onion_extend_cpath(circ->purpose, &circ->cpath, circ->build_state);
-// || !CIRCUIT_IS_ORIGIN(circ)) { // wtf? -rd
+ r = onion_extend_cpath(circ->_base.purpose, &circ->cpath, circ->build_state);
if (r < 0) {
log_info(LD_CIRC,"Generating cpath hop failed.");
return -1;
@@ -284,19 +221,20 @@ again:
return 0; /* if r == 1 */
}
-/** Create and return a new circuit. Initialize its purpose and
+/** Create and return a new origin circuit. Initialize its purpose and
* build-state based on our arguments. */
-circuit_t *
-circuit_init(uint8_t purpose, int need_uptime, int need_capacity, int internal)
+origin_circuit_t *
+origin_circuit_init(uint8_t purpose, int need_uptime, int need_capacity,
+ int internal)
{
/* sets circ->p_circ_id and circ->p_conn */
- circuit_t *circ = circuit_new(0, NULL);
- circuit_set_state(circ, CIRCUIT_STATE_OR_WAIT);
+ origin_circuit_t *circ = origin_circuit_new();
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OR_WAIT);
circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
circ->build_state->need_uptime = need_uptime;
circ->build_state->need_capacity = need_capacity;
circ->build_state->is_internal = internal;
- circ->purpose = purpose;
+ circ->_base.purpose = purpose;
return circ;
}
@@ -307,24 +245,24 @@ circuit_init(uint8_t purpose, int need_uptime, int need_capacity, int internal)
* Also launch a connection to the first OR in the chosen path, if
* it's not open already.
*/
-circuit_t *
+origin_circuit_t *
circuit_establish_circuit(uint8_t purpose, extend_info_t *info,
int need_uptime, int need_capacity, int internal)
{
- circuit_t *circ;
+ origin_circuit_t *circ;
- circ = circuit_init(purpose, need_uptime, need_capacity, internal);
+ circ = origin_circuit_init(purpose, need_uptime, need_capacity, internal);
if (onion_pick_cpath_exit(circ, info) < 0 ||
onion_populate_cpath(circ) < 0) {
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return NULL;
}
control_event_circuit_status(circ, CIRC_EVENT_LAUNCHED);
if (circuit_handle_first_hop(circ) < 0) {
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return NULL;
}
return circ;
@@ -335,10 +273,10 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *info,
* it. If we're already connected, then send the 'create' cell.
* Return 0 for ok, -1 if circ should be marked-for-close. */
int
-circuit_handle_first_hop(circuit_t *circ)
+circuit_handle_first_hop(origin_circuit_t *circ)
{
crypt_path_t *firsthop;
- connection_t *n_conn;
+ or_connection_t *n_conn;
char tmpbuf[INET_NTOA_BUF_LEN];
struct in_addr in;
@@ -352,22 +290,22 @@ circuit_handle_first_hop(circuit_t *circ)
log_debug(LD_CIRC,"Looking for firsthop '%s:%u'",tmpbuf,
firsthop->extend_info->port);
/* imprint the circuit with its future n_conn->id */
- memcpy(circ->n_conn_id_digest, firsthop->extend_info->identity_digest,
+ memcpy(circ->_base.n_conn_id_digest, firsthop->extend_info->identity_digest,
DIGEST_LEN);
n_conn = connection_or_get_by_identity_digest(
firsthop->extend_info->identity_digest);
/* If we don't have an open conn, or the conn we have is obsolete
* (i.e. old or broken) and the other side will let us make a second
* connection without dropping it immediately... */
- if (!n_conn || n_conn->state != OR_CONN_STATE_OPEN ||
- (n_conn->is_obsolete &&
+ if (!n_conn || n_conn->_base.state != OR_CONN_STATE_OPEN ||
+ (n_conn->_base.or_is_obsolete &&
router_digest_version_as_new_as(firsthop->extend_info->identity_digest,
"0.1.1.9-alpha-cvs"))) {
/* not currently connected */
- circ->n_addr = firsthop->extend_info->addr;
- circ->n_port = firsthop->extend_info->port;
+ circ->_base.n_addr = firsthop->extend_info->addr;
+ circ->_base.n_port = firsthop->extend_info->port;
- if (!n_conn || n_conn->is_obsolete) { /* launch the connection */
+ if (!n_conn || n_conn->_base.or_is_obsolete) { /* launch the connection */
n_conn = connection_or_connect(firsthop->extend_info->addr,
firsthop->extend_info->port,
firsthop->extend_info->identity_digest);
@@ -384,9 +322,9 @@ circuit_handle_first_hop(circuit_t *circ)
*/
return 0;
} else { /* it's already open. use it. */
- circ->n_addr = n_conn->addr;
- circ->n_port = n_conn->port;
- circ->n_conn = n_conn;
+ circ->_base.n_addr = n_conn->_base.addr;
+ circ->_base.n_port = n_conn->_base.port;
+ circ->_base.n_conn = n_conn;
log_debug(LD_CIRC,"Conn open. Delivering first onion skin.");
if (circuit_send_next_onion_skin(circ) < 0) {
log_info(LD_CIRC,"circuit_send_next_onion_skin failed.");
@@ -396,15 +334,17 @@ circuit_handle_first_hop(circuit_t *circ)
return 0;
}
+extern smartlist_t *circuits_pending_or_conns;
+
/** Find any circuits that are waiting on <b>or_conn</b> to become
* open and get them to send their create cells forward.
*
* Status is 1 if connect succeeded, or 0 if connect failed.
*/
void
-circuit_n_conn_done(connection_t *or_conn, int status)
+circuit_n_conn_done(or_connection_t *or_conn, int status)
{
- extern smartlist_t *circuits_pending_or_conns;
+ smartlist_t *changed_circs;
smartlist_t *changed_circs;
log_debug(LD_CIRC,"or_conn to %s, status=%d",
@@ -428,14 +368,13 @@ circuit_n_conn_done(connection_t *or_conn, int status)
circuit_mark_for_close(circ, END_CIRC_REASON_OR_CONN_CLOSED);
continue;
}
- log_debug(LD_CIRC,
- "Found circ %d, sending create cell.", circ->n_circ_id);
+ log_debug(LD_CIRC, "Found circ, sending create cell.");
/* circuit_deliver_create_cell will set n_circ_id and add us to
* orconn_circuid_circuit_map, so we don't need to call
* set_circid_orconn here. */
circ->n_conn = or_conn;
if (CIRCUIT_IS_ORIGIN(circ)) {
- if (circuit_send_next_onion_skin(circ) < 0) {
+ if (circuit_send_next_onion_skin(TO_ORIGIN_CIRCUIT(circ)) < 0) {
log_info(LD_CIRC,
"send_next_onion_skin failed; circuit marked for closing.");
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
@@ -473,14 +412,14 @@ circuit_n_conn_done(connection_t *or_conn, int status)
* Return -1 if we failed to find a suitable circid, else return 0.
*/
static int
-circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type, char *payload)
+circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type,
+ char *payload)
{
cell_t cell;
uint16_t id;
tor_assert(circ);
tor_assert(circ->n_conn);
- tor_assert(circ->n_conn->type == CONN_TYPE_OR);
tor_assert(payload);
tor_assert(cell_type == CELL_CREATE || cell_type == CELL_CREATE_FAST);
@@ -490,7 +429,7 @@ circuit_deliver_create_cell(circuit_t *circ, uint8_t cell_type, char *payload)
return -1;
}
log_debug(LD_CIRC,"Chosen circID %u.", id);
- circuit_set_circid_orconn(circ, id, circ->n_conn, N_CONN_CHANGED);
+ circuit_set_n_circid_orconn(circ, id, circ->n_conn);
memset(&cell, 0, sizeof(cell_t));
cell.command = cell_type;
@@ -526,13 +465,13 @@ inform_testing_reachability(void)
/** Return true iff we should send a create_fast cell to build a circuit
* starting at <b>router</b>. (If <b>router</b> is NULL, we don't have
- * information on the router. */
+ * information on the router, so return false.) */
static INLINE int
should_use_create_fast_for_router(routerinfo_t *router)
{
or_options_t *options = get_options();
- if (!options->FastFirstHopPK || options->ORPort)
+ if (!options->FastFirstHopPK || server_mode(options))
return 0;
else if (!router || !router->platform ||
!tor_version_as_new_as(router->platform, "0.1.0.6-rc"))
@@ -541,8 +480,6 @@ should_use_create_fast_for_router(routerinfo_t *router)
return 1;
}
-extern int has_completed_circuit;
-
/** This is the backbone function for building circuits.
*
* If circ's first hop is closed, then we need to build a create
@@ -554,7 +491,7 @@ extern int has_completed_circuit;
* Return -reason if we want to tear down circ, else return 0.
*/
int
-circuit_send_next_onion_skin(circuit_t *circ)
+circuit_send_next_onion_skin(origin_circuit_t *circ)
{
crypt_path_t *hop;
routerinfo_t *router;
@@ -563,14 +500,13 @@ circuit_send_next_onion_skin(circuit_t *circ)
size_t payload_len;
tor_assert(circ);
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
if (circ->cpath->state == CPATH_STATE_CLOSED) {
int fast;
uint8_t cell_type;
log_debug(LD_CIRC,"First skin; sending create cell.");
- router = router_get_by_digest(circ->n_conn->identity_digest);
+ router = router_get_by_digest(circ->_base.n_conn->identity_digest);
fast = should_use_create_fast_for_router(router);
if (! fast) {
/* We are an OR, or we are connecting to an old Tor: we should
@@ -595,22 +531,22 @@ circuit_send_next_onion_skin(circuit_t *circ)
sizeof(circ->cpath->fast_handshake_state));
}
- if (circuit_deliver_create_cell(circ, cell_type, payload) < 0)
+ if (circuit_deliver_create_cell(TO_CIRCUIT(circ), cell_type, payload) < 0)
return - END_CIRC_REASON_RESOURCELIMIT;
circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
- circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'",
fast ? "CREATE_FAST" : "CREATE",
- router ? router->nickname : "<unnamed>");
+ router ? router->nickname : "<unnamed>");
} else {
tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
- tor_assert(circ->state == CIRCUIT_STATE_BUILDING);
+ tor_assert(circ->_base.state == CIRCUIT_STATE_BUILDING);
log_debug(LD_CIRC,"starting to send subsequent skin.");
hop = onion_next_hop_in_cpath(circ->cpath);
if (!hop) {
/* done building the circuit. whew. */
- circuit_set_state(circ, CIRCUIT_STATE_OPEN);
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
log_info(LD_CIRC,"circuit built!");
circuit_reset_failure_count(0);
if (!has_completed_circuit) {
@@ -622,7 +558,7 @@ circuit_send_next_onion_skin(circuit_t *circ)
"Looks like client functionality is working.");
if (server_mode(options) && !check_whether_orport_reachable()) {
inform_testing_reachability();
- consider_testing_reachability();
+ consider_testing_reachability(1, 1);
}
}
circuit_rep_hist_note_result(circ);
@@ -647,7 +583,8 @@ circuit_send_next_onion_skin(circuit_t *circ)
log_debug(LD_CIRC,"Sending extend relay cell.");
/* send it to hop->prev, because it will transfer
* it to a create cell and then send to hop */
- if (connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTEND,
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(circ),
+ RELAY_COMMAND_EXTEND,
payload, payload_len, hop->prev) < 0)
return 0; /* circuit is closed */
@@ -662,8 +599,13 @@ circuit_send_next_onion_skin(circuit_t *circ)
void
circuit_note_clock_jumped(int seconds_elapsed)
{
- log(LOG_NOTICE, LD_GENERAL,"Your clock just jumped %d seconds forward; "
- "assuming established circuits no longer work.", seconds_elapsed);
+ if (server_mode(get_options()))
+ log(LOG_WARN, LD_GENERAL,
+ "Please report: your clock just jumped %d seconds forward; "
+ "assuming established circuits no longer work.", seconds_elapsed);
+ else
+ log(LOG_NOTICE, LD_GENERAL, "Your clock just jumped %d seconds forward; "
+ "assuming established circuits no longer work.", seconds_elapsed);
has_completed_circuit=0; /* so it'll log when it works again */
circuit_mark_all_unused_circs();
circuit_expire_all_dirty_circs();
@@ -677,7 +619,7 @@ circuit_note_clock_jumped(int seconds_elapsed)
int
circuit_extend(cell_t *cell, circuit_t *circ)
{
- connection_t *n_conn;
+ or_connection_t *n_conn;
relay_header_t rh;
char *onionskin;
char *id_digest=NULL;
@@ -693,6 +635,12 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return -1;
}
+ if (!server_mode(get_options())) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Got an extend cell, but running as a client. Closing.");
+ return -1;
+ }
+
relay_header_unpack(&rh, cell->payload);
if (rh.length < 4+2+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN) {
@@ -712,12 +660,9 @@ circuit_extend(cell_t *cell, circuit_t *circ)
/* If we don't have an open conn, or the conn we have is obsolete
* (i.e. old or broken) and the other side will let us make a second
* connection without dropping it immediately... */
- if (!n_conn || n_conn->state != OR_CONN_STATE_OPEN ||
- (n_conn->is_obsolete &&
+ if (!n_conn || n_conn->_base.state != OR_CONN_STATE_OPEN ||
+ (n_conn->_base.or_is_obsolete &&
router_digest_version_as_new_as(id_digest,"0.1.1.9-alpha-cvs"))) {
- /* Note that this will close circuits where the onion has the same
- * router twice in a row in the path. I think that's ok.
- */
struct in_addr in;
char tmpbuf[INET_NTOA_BUF_LEN];
in.s_addr = htonl(circ->n_addr);
@@ -732,9 +677,9 @@ circuit_extend(cell_t *cell, circuit_t *circ)
/* imprint the circuit with its future n_conn->id */
memcpy(circ->n_conn_id_digest, id_digest, DIGEST_LEN);
- if (n_conn && !n_conn->is_obsolete) {
- circ->n_addr = n_conn->addr;
- circ->n_port = n_conn->port;
+ if (n_conn && !n_conn->_base.or_is_obsolete) {
+ circ->n_addr = n_conn->_base.addr;
+ circ->n_port = n_conn->_base.port;
} else {
/* we should try to open a connection */
n_conn = connection_or_connect(circ->n_addr, circ->n_port, id_digest);
@@ -753,12 +698,13 @@ circuit_extend(cell_t *cell, circuit_t *circ)
}
/* these may be different if the router connected to us from elsewhere */
- circ->n_addr = n_conn->addr;
- circ->n_port = n_conn->port;
+ circ->n_addr = n_conn->_base.addr;
+ circ->n_port = n_conn->_base.port;
circ->n_conn = n_conn;
memcpy(circ->n_conn_id_digest, n_conn->identity_digest, DIGEST_LEN);
- log_debug(LD_CIRC,"n_conn is %s:%u",n_conn->address,n_conn->port);
+ log_debug(LD_CIRC,"n_conn is %s:%u",
+ n_conn->_base.address,n_conn->_base.port);
if (circuit_deliver_create_cell(circ, CELL_CREATE, onionskin) < 0)
return -1;
@@ -825,12 +771,12 @@ circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse)
* Return - reason if we want to mark circ for close, else return 0.
*/
int
-circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
+circuit_finish_handshake(origin_circuit_t *circ, uint8_t reply_type,
+ char *reply)
{
char keys[CPATH_KEY_MATERIAL_LEN];
crypt_path_t *hop;
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
if (circ->cpath->state == CPATH_STATE_AWAITING_KEYS)
hop = circ->cpath;
else {
@@ -888,20 +834,19 @@ circuit_finish_handshake(circuit_t *circ, uint8_t reply_type, char *reply)
* just give up: for circ to close, and return 0.
*/
int
-circuit_truncated(circuit_t *circ, crypt_path_t *layer)
+circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer)
{
// crypt_path_t *victim;
// connection_t *stream;
tor_assert(circ);
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
tor_assert(layer);
/* XXX Since we don't ask for truncates currently, getting a truncated
* means that a connection broke or an extend failed. For now,
* just give up.
*/
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return 0;
#if 0
@@ -934,7 +879,8 @@ circuit_truncated(circuit_t *circ, crypt_path_t *layer)
* cell back.
*/
int
-onionskin_answer(circuit_t *circ, uint8_t cell_type, char *payload, char *keys)
+onionskin_answer(or_circuit_t *circ, uint8_t cell_type, char *payload,
+ char *keys)
{
cell_t cell;
crypt_path_t *tmp_cpath;
@@ -946,7 +892,7 @@ onionskin_answer(circuit_t *circ, uint8_t cell_type, char *payload, char *keys)
cell.command = cell_type;
cell.circ_id = circ->p_circ_id;
- circuit_set_state(circ, CIRCUIT_STATE_OPEN);
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
memcpy(cell.payload, payload,
cell_type == CELL_CREATED ? ONIONSKIN_REPLY_LEN : DIGEST_LEN*2);
@@ -974,8 +920,8 @@ onionskin_answer(circuit_t *circ, uint8_t cell_type, char *payload, char *keys)
connection_or_write_cell_to_buf(&cell, circ->p_conn);
log_debug(LD_CIRC,"Finished sending 'created' cell.");
- if (!is_local_IP(circ->p_conn->addr) &&
- tor_tls_is_server(circ->p_conn->tls)) {
+ if (!is_local_IP(circ->p_conn->_base.addr) &&
+ !connection_or_nonopen_was_started_here(circ->p_conn)) {
/* record that we could process create cells from a non-local conn
* that we didn't initiate; presumably this means that create cells
* can reach us too. */
@@ -1112,8 +1058,9 @@ ap_stream_wants_exit_attention(connection_t *conn)
if (conn->type == CONN_TYPE_AP &&
conn->state == AP_CONN_STATE_CIRCUIT_WAIT &&
!conn->marked_for_close &&
- !connection_edge_is_rendezvous_stream(conn) &&
- !circuit_stream_is_being_handled(conn, 0, MIN_CIRCUITS_HANDLING_STREAM))
+ !connection_edge_is_rendezvous_stream(TO_EDGE_CONN(conn)) &&
+ !circuit_stream_is_being_handled(TO_EDGE_CONN(conn), 0,
+ MIN_CIRCUITS_HANDLING_STREAM))
return 1;
return 0;
}
@@ -1198,7 +1145,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
for (j = 0; j < n_connections; ++j) { /* iterate over connections */
if (!ap_stream_wants_exit_attention(carray[j]))
continue; /* Skip everything but APs in CIRCUIT_WAIT */
- if (connection_ap_can_use_exit(carray[j], router)) {
+ if (connection_ap_can_use_exit(TO_EDGE_CONN(carray[j]), router)) {
++n_supported[i];
// log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
// router->nickname, i, n_supported[i]);
@@ -1260,7 +1207,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
need_uptime?", stable":"");
return choose_good_exit_server_general(dir, 0, 0);
}
- log_notice(LD_CIRC, "All routers are down or middleman -- choosing a "
+ log_notice(LD_CIRC, "All routers are down or won't exit -- choosing a "
"doomed exit at random.");
}
for (try = 0; try < 2; try++) {
@@ -1343,13 +1290,13 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
* router (or use <b>exit</b> if provided). Store these in the
* cpath. Return 0 if ok, -1 if circuit should be closed. */
static int
-onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit)
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
{
cpath_build_state_t *state = circ->build_state;
routerlist_t *rl = router_get_routerlist();
int r;
- r = new_route_len(get_options()->PathlenCoinWeight, circ->purpose,
+ r = new_route_len(get_options()->PathlenCoinWeight, circ->_base.purpose,
exit, rl->routers);
if (r < 1) /* must be at least 1 */
return -1;
@@ -1360,8 +1307,8 @@ onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit)
exit = extend_info_dup(exit);
} else { /* we have to decide one */
routerinfo_t *router =
- choose_good_exit_server(circ->purpose, rl, state->need_uptime,
- state->need_capacity, state->is_internal);
+ choose_good_exit_server(circ->_base.purpose, rl, state->need_uptime,
+ state->need_capacity, state->is_internal);
if (!router) {
log_warn(LD_CIRC,"failed to choose an exit server");
return -1;
@@ -1377,11 +1324,11 @@ onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit)
* the caller will do this if it wants to.
*/
int
-circuit_append_new_exit(circuit_t *circ, extend_info_t *info)
+circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info)
{
cpath_build_state_t *state;
tor_assert(info);
- tor_assert(circ && CIRCUIT_IS_ORIGIN(circ));
+ tor_assert(circ);
state = circ->build_state;
tor_assert(state);
@@ -1399,15 +1346,14 @@ circuit_append_new_exit(circuit_t *circ, extend_info_t *info)
* send the next extend cell to begin connecting to that hop.
*/
int
-circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info)
+circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info)
{
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
circuit_append_new_exit(circ, info);
- circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
if (circuit_send_next_onion_skin(circ)<0) {
log_warn(LD_CIRC, "Couldn't extend circuit to new point '%s'.",
info->nickname);
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
return 0;
@@ -1436,6 +1382,10 @@ count_acceptable_routers(smartlist_t *routers)
if (r->is_valid == 0) {
// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
goto next_i_loop;
+ /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
+ * allows this node in some places, then we're getting an inaccurate
+ * count. For now, be conservative and don't count it. But later we
+ * should try to be smarter. */
}
num++;
// log_debug(LD_CIRC,"I like %d. num_acceptable_routers now %d.",i, num);
@@ -1472,7 +1422,7 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
* whole network has upgraded. */
static char *
compute_preferred_testing_list(const char *answer)
-{
+{
smartlist_t *choices;
routerlist_t *rl = router_get_routerlist();
routerinfo_t *router;
@@ -1569,6 +1519,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
routerinfo_t *r, *choice;
smartlist_t *excluded;
or_options_t *options = get_options();
+ (void)purpose; /* not used yet. */
if (state && options->UseEntryGuards) {
return choose_random_entry(state);
@@ -1835,7 +1786,7 @@ log_entry_guards(int severity)
{
tor_snprintf(buf, sizeof(buf), "%s (%s%s%s)",
e->nickname,
- e->down_since ? "down " : "up ",
+ (e->down_since || e->unlisted_since) ? "down " : "up ",
e->unlisted_since ? "unlisted " : "listed ",
e->made_contact ? "made-contact" : "never-contacted");
smartlist_add(elements, tor_strdup(buf));
@@ -1850,7 +1801,7 @@ log_entry_guards(int severity)
#define NUM_ENTRY_PICK_TRIES 100
-/** Add a new (preferably stable and fast) entry to our
+/** Add a new (preferably stable and fast) router to our
* entry_guards list. Return a pointer to the router if we succeed,
* or NULL if we can't find any more suitable entries.
*
@@ -1862,33 +1813,23 @@ add_an_entry_guard(routerinfo_t *chosen)
{
routerinfo_t *router;
entry_guard_t *entry;
- int tries_left = NUM_ENTRY_PICK_TRIES;
-again:
- if (--tries_left <= 0) {
- log_warn(LD_CIRC, "Tried finding a new entry guard, but failed. "
- "Can you reach the Tor network?");
- return NULL;
- }
- if (chosen)
+ if (chosen) {
router = chosen;
- else
+ if (is_an_entry_guard(router->cache_info.identity_digest))
+ return NULL;
+ } else {
router = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
- if (!router)
- return NULL;
- /* make sure it's not already an entry */
- if (is_an_entry_guard(router->cache_info.identity_digest)) {
- if (chosen)
+ if (!router)
return NULL;
- goto again;
}
entry = tor_malloc_zero(sizeof(entry_guard_t));
log_info(LD_CIRC, "Chose '%s' as new entry guard.", router->nickname);
strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname));
memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN);
- if (chosen)
+ if (chosen) /* prepend */
smartlist_insert(entry_guards, 0, entry);
- else
+ else /* append */
smartlist_add(entry_guards, entry);
log_entry_guards(LOG_INFO);
return router;
@@ -2032,7 +1973,7 @@ entry_guards_set_status_from_directory(void)
}
log_info(LD_CIRC, "Summary: Entry '%s' is %s, %s, and %s.",
entry->nickname,
- entry->down_since ? "down" : "up",
+ (entry->down_since || entry->unlisted_since) ? "down" : "up",
entry->unlisted_since ? "unlisted" : "listed",
entry_is_live(entry, 0, 1) ? "live" : "not live");
});
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 73513c4030..dba7864861 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -33,7 +33,7 @@ static void circuit_free_cpath_node(crypt_path_t *victim);
* very important here, since we need to do it every time a cell arrives.) */
typedef struct orconn_circid_circuit_map_t {
HT_ENTRY(orconn_circid_circuit_map_t) node;
- connection_t *or_conn;
+ or_connection_t *or_conn;
uint16_t circ_id;
circuit_t *circuit;
} orconn_circid_circuit_map_t;
@@ -68,35 +68,14 @@ HT_GENERATE(orconn_circid_map, orconn_circid_circuit_map_t, node,
*/
orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL;
-/** Set the p_conn or n_conn field of a circuit <b>circ</b>, along
- * with the corresponding circuit ID, and add the circuit as appropriate
- * to the (orconn,id)-\>circuit map. */
-void
-circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
- connection_t *conn,
- enum which_conn_changed_t which)
+static void
+circuit_set_circid_orconn_helper(circuit_t *circ, uint16_t id,
+ or_connection_t *conn,
+ uint16_t old_id, or_connection_t *old_conn)
{
- uint16_t old_id;
- connection_t *old_conn;
orconn_circid_circuit_map_t search;
orconn_circid_circuit_map_t *found;
- tor_assert(!conn || conn->type == CONN_TYPE_OR);
-
- if (which == P_CONN_CHANGED) {
- old_id = circ->p_circ_id;
- old_conn = circ->p_conn;
- circ->p_circ_id = id;
- circ->p_conn = conn;
- } else {
- old_id = circ->n_circ_id;
- old_conn = circ->n_conn;
- circ->n_circ_id = id;
- circ->n_conn = conn;
- }
- if (conn == old_conn && old_id == id)
- return;
-
if (_last_circid_orconn_ent &&
((old_id == _last_circid_orconn_ent->circ_id &&
old_conn == _last_circid_orconn_ent->or_conn) ||
@@ -106,7 +85,7 @@ circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
}
if (old_conn) { /* we may need to remove it from the conn-circid map */
- tor_assert(old_conn->magic == CONNECTION_MAGIC);
+ tor_assert(old_conn->_base.magic == OR_CONNECTION_MAGIC);
search.circ_id = old_id;
search.or_conn = old_conn;
found = HT_REMOVE(orconn_circid_map, &orconn_circid_circuit_map, &search);
@@ -135,6 +114,47 @@ circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
++conn->n_circuits;
}
+/** Set the p_conn field of a circuit <b>circ</b>, along
+ * with the corresponding circuit ID, and add the circuit as appropriate
+ * to the (orconn,id)-\>circuit map. */
+void
+circuit_set_p_circid_orconn(or_circuit_t *circ, uint16_t id,
+ or_connection_t *conn)
+{
+ uint16_t old_id;
+ or_connection_t *old_conn;
+
+ old_id = circ->p_circ_id;
+ old_conn = circ->p_conn;
+ circ->p_circ_id = id;
+ circ->p_conn = conn;
+
+ if (id == old_id && conn == old_conn)
+ return;
+ circuit_set_circid_orconn_helper(TO_CIRCUIT(circ), id, conn,
+ old_id, old_conn);
+}
+
+/** Set the n_conn field of a circuit <b>circ</b>, along
+ * with the corresponding circuit ID, and add the circuit as appropriate
+ * to the (orconn,id)-\>circuit map. */
+void
+circuit_set_n_circid_orconn(circuit_t *circ, uint16_t id,
+ or_connection_t *conn)
+{
+ uint16_t old_id;
+ or_connection_t *old_conn;
+
+ old_id = circ->n_circ_id;
+ old_conn = circ->n_conn;
+ circ->n_circ_id = id;
+ circ->n_conn = conn;
+
+ if (id == old_id && conn == old_conn)
+ return;
+ circuit_set_circid_orconn_helper(circ, id, conn, old_id, old_conn);
+}
+
/** Change the state of <b>circ</b> to <b>state</b>, adding it to or removing
* it from lists as appropriate. */
void
@@ -200,7 +220,7 @@ circuit_close_all_marked(void)
}
}
-/** Return the head of the global linked list of circuits. **/
+/** Return the head of the global linked list of circuits. */
circuit_t *
_circuit_get_global_list(void)
{
@@ -224,36 +244,53 @@ circuit_state_to_string(int state)
}
}
+/* DOCDOC */
+static void
+init_circuit_base(circuit_t *circ)
+{
+ circ->timestamp_created = time(NULL);
+
+ circ->package_window = CIRCWINDOW_START;
+ circ->deliver_window = CIRCWINDOW_START;
+
+ circuit_add(circ);
+}
+
/** Allocate space for a new circuit, initializing with <b>p_circ_id</b>
* and <b>p_conn</b>. Add it to the global circuit list.
*/
-circuit_t *
-circuit_new(uint16_t p_circ_id, connection_t *p_conn)
+origin_circuit_t *
+origin_circuit_new(void)
{
- circuit_t *circ;
- static uint32_t n_circuits_allocated = 1;
+ origin_circuit_t *circ;
/* never zero, since a global ID of 0 is treated specially by the
* controller */
+ static uint32_t n_circuits_allocated = 1;
- circ = tor_malloc_zero(sizeof(circuit_t));
- circ->magic = CIRCUIT_MAGIC;
+ circ = tor_malloc_zero(sizeof(origin_circuit_t));
+ circ->_base.magic = ORIGIN_CIRCUIT_MAGIC;
- circ->timestamp_created = time(NULL);
+ circ->next_stream_id = crypto_rand_int(1<<16);
+ circ->global_identifier = n_circuits_allocated++;
+ init_circuit_base(TO_CIRCUIT(circ));
+
+ return circ;
+}
+
+or_circuit_t *
+or_circuit_new(uint16_t p_circ_id, or_connection_t *p_conn)
+{
/* CircIDs */
- if (p_conn) {
- circuit_set_circid_orconn(circ, p_circ_id, p_conn, P_CONN_CHANGED);
- }
- /* circ->n_circ_id remains 0 because we haven't identified the next hop
- * yet */
+ or_circuit_t *circ;
- circ->package_window = CIRCWINDOW_START;
- circ->deliver_window = CIRCWINDOW_START;
+ circ = tor_malloc_zero(sizeof(or_circuit_t));
+ circ->_base.magic = OR_CIRCUIT_MAGIC;
- circ->next_stream_id = crypto_rand_int(1<<16);
- circ->global_identifier = n_circuits_allocated++;
+ if (p_conn)
+ circuit_set_p_circid_orconn(circ, p_circ_id, p_conn);
- circuit_add(circ);
+ init_circuit_base(TO_CIRCUIT(circ));
return circ;
}
@@ -263,35 +300,53 @@ circuit_new(uint16_t p_circ_id, connection_t *p_conn)
static void
circuit_free(circuit_t *circ)
{
+ void *mem;
tor_assert(circ);
- tor_assert(circ->magic == CIRCUIT_MAGIC);
- if (circ->n_crypto)
- crypto_free_cipher_env(circ->n_crypto);
- if (circ->p_crypto)
- crypto_free_cipher_env(circ->p_crypto);
- if (circ->n_digest)
- crypto_free_digest_env(circ->n_digest);
- if (circ->p_digest)
- crypto_free_digest_env(circ->p_digest);
- if (circ->build_state) {
- if (circ->build_state->chosen_exit)
- extend_info_free(circ->build_state->chosen_exit);
- if (circ->build_state->pending_final_cpath)
- circuit_free_cpath_node(circ->build_state->pending_final_cpath);
- }
- tor_free(circ->build_state);
- tor_free(circ->onionskin);
- circuit_free_cpath(circ->cpath);
- if (circ->rend_splice) {
- tor_assert(circ->rend_splice->magic == CIRCUIT_MAGIC);
- circ->rend_splice->rend_splice = NULL;
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ mem = ocirc;
+ tor_assert(circ->magic == ORIGIN_CIRCUIT_MAGIC);
+ if (ocirc->build_state) {
+ if (ocirc->build_state->chosen_exit)
+ extend_info_free(ocirc->build_state->chosen_exit);
+ if (ocirc->build_state->pending_final_cpath)
+ circuit_free_cpath_node(ocirc->build_state->pending_final_cpath);
+ }
+ tor_free(ocirc->build_state);
+
+ circuit_free_cpath(ocirc->cpath);
+
+ } else {
+ or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
+ mem = ocirc;
+ tor_assert(circ->magic == OR_CIRCUIT_MAGIC);
+
+ if (ocirc->p_crypto)
+ crypto_free_cipher_env(ocirc->p_crypto);
+ if (ocirc->p_digest)
+ crypto_free_digest_env(ocirc->p_digest);
+ if (ocirc->n_crypto)
+ crypto_free_cipher_env(ocirc->n_crypto);
+ if (ocirc->n_digest)
+ crypto_free_digest_env(ocirc->n_digest);
+
+ if (ocirc->rend_splice) {
+ or_circuit_t *other = ocirc->rend_splice;
+ tor_assert(other->_base.magic == OR_CIRCUIT_MAGIC);
+ other->rend_splice = NULL;
+ }
+
+ tor_free(circ->onionskin);
+
+ /* remove from map. */
+ circuit_set_p_circid_orconn(ocirc, 0, NULL);
}
+
/* Remove from map. */
- circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
- circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
+ circuit_set_n_circid_orconn(circ, 0, NULL);
memset(circ, 0xAA, sizeof(circuit_t)); /* poison memory */
- tor_free(circ);
+ tor_free(mem);
}
/** Deallocate space associated with the linked list <b>cpath</b>. */
@@ -321,11 +376,14 @@ circuit_free_all(void)
circuit_t *next;
while (global_circuitlist) {
next = global_circuitlist->next;
- while (global_circuitlist->resolving_streams) {
- connection_t *next;
- next = global_circuitlist->resolving_streams->next_stream;
- connection_free(global_circuitlist->resolving_streams);
- global_circuitlist->resolving_streams = next;
+ if (! CIRCUIT_IS_ORIGIN(global_circuitlist)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(global_circuitlist);
+ while (or_circ->resolving_streams) {
+ edge_connection_t *next;
+ next = or_circ->resolving_streams->next_stream;
+ connection_free(TO_CONN(or_circ->resolving_streams));
+ or_circ->resolving_streams = next;
+ }
}
circuit_free(global_circuitlist);
global_circuitlist = next;
@@ -358,18 +416,92 @@ circuit_free_cpath_node(crypt_path_t *victim)
tor_free(victim);
}
+/** A helper function for circuit_dump_by_conn() below. Log a bunch
+ * of information about circuit <b>circ</b>.
+ */
+static void
+circuit_dump_details(int severity, circuit_t *circ, int conn_array_index,
+ const char *type, int this_circid, int other_circid)
+{
+ log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), "
+ "state %d (%s), born %d:",
+ conn_array_index, type, this_circid, other_circid, circ->state,
+ circuit_state_to_string(circ->state), (int)circ->timestamp_created);
+ if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */
+ circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ));
+ }
+}
+
+/** Log, at severity <b>severity</b>, information about each circuit
+ * that is connected to <b>conn</b>.
+ */
+void
+circuit_dump_by_conn(connection_t *conn, int severity)
+{
+ circuit_t *circ;
+ edge_connection_t *tmpconn;
+
+ for (circ=global_circuitlist;circ;circ = circ->next) {
+ circid_t n_circ_id = circ->n_circ_id, p_circ_id = 0;
+ if (circ->marked_for_close)
+ continue;
+
+ if (! CIRCUIT_IS_ORIGIN(circ))
+ p_circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
+
+ if (! CIRCUIT_IS_ORIGIN(circ) && TO_OR_CIRCUIT(circ)->p_conn &&
+ TO_CONN(TO_OR_CIRCUIT(circ)->p_conn) == conn)
+ circuit_dump_details(severity, circ, conn->conn_array_index, "App-ward",
+ p_circ_id, n_circ_id);
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ for (tmpconn=TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn;
+ tmpconn=tmpconn->next_stream) {
+ if (TO_CONN(tmpconn) == conn) {
+ circuit_dump_details(severity, circ, conn->conn_array_index,
+ "App-ward", p_circ_id, n_circ_id);
+ }
+ }
+ }
+ if (circ->n_conn && TO_CONN(circ->n_conn) == conn)
+ circuit_dump_details(severity, circ, conn->conn_array_index, "Exit-ward",
+ n_circ_id, p_circ_id);
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ for (tmpconn=TO_OR_CIRCUIT(circ)->n_streams; tmpconn;
+ tmpconn=tmpconn->next_stream) {
+ if (TO_CONN(tmpconn) == conn) {
+ circuit_dump_details(severity, circ, conn->conn_array_index,
+ "Exit-ward", n_circ_id, p_circ_id);
+ }
+ }
+ }
+ if (!circ->n_conn && circ->n_addr && circ->n_port &&
+ circ->n_addr == conn->addr &&
+ circ->n_port == conn->port &&
+ conn->type == CONN_TYPE_OR &&
+ !memcmp(TO_OR_CONN(conn)->identity_digest, circ->n_conn_id_digest,
+ DIGEST_LEN)) {
+ circuit_dump_details(severity, circ, conn->conn_array_index,
+ (circ->state == CIRCUIT_STATE_OPEN &&
+ !CIRCUIT_IS_ORIGIN(circ)) ?
+ "Endpoint" : "Pending",
+ n_circ_id, p_circ_id);
+ }
+ }
+}
+
/** Return the circuit whose global ID is <b>id</b>, or NULL if no
* such circuit exists. */
-circuit_t *
+origin_circuit_t *
circuit_get_by_global_id(uint32_t id)
{
circuit_t *circ;
for (circ=global_circuitlist;circ;circ = circ->next) {
- if (circ->global_identifier == id) {
+ if (CIRCUIT_IS_ORIGIN(circ) &&
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier == id) {
if (circ->marked_for_close)
return NULL;
else
- return circ;
+ return TO_ORIGIN_CIRCUIT(circ);
}
}
return NULL;
@@ -381,13 +513,11 @@ circuit_get_by_global_id(uint32_t id)
* Return NULL if no such circuit exists.
*/
static INLINE circuit_t *
-circuit_get_by_circid_orconn_impl(uint16_t circ_id, connection_t *conn)
+circuit_get_by_circid_orconn_impl(uint16_t circ_id, or_connection_t *conn)
{
orconn_circid_circuit_map_t search;
orconn_circid_circuit_map_t *found;
- tor_assert(conn->type == CONN_TYPE_OR);
-
if (_last_circid_orconn_ent &&
circ_id == _last_circid_orconn_ent->circ_id &&
conn == _last_circid_orconn_ent->or_conn) {
@@ -407,10 +537,13 @@ circuit_get_by_circid_orconn_impl(uint16_t circ_id, connection_t *conn)
{
circuit_t *circ;
for (circ=global_circuitlist;circ;circ = circ->next) {
- if (circ->p_conn == conn && circ->p_circ_id == circ_id) {
- log_warn(LD_BUG,
- "circuit matches p_conn, but not in hash table (Bug!)");
- return circ;
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (or_circ->p_conn == conn && or_circ->p_circ_id == circ_id) {
+ log_warn(LD_BUG,
+ "circuit matches p_conn, but not in hash table (Bug!)");
+ return circ;
+ }
}
if (circ->n_conn == conn && circ->n_circ_id == circ_id) {
log_warn(LD_BUG,
@@ -429,7 +562,7 @@ circuit_get_by_circid_orconn_impl(uint16_t circ_id, connection_t *conn)
* Return NULL if no such circuit exists.
*/
circuit_t *
-circuit_get_by_circid_orconn(uint16_t circ_id, connection_t *conn)
+circuit_get_by_circid_orconn(uint16_t circ_id, or_connection_t *conn)
{
circuit_t *circ = circuit_get_by_circid_orconn_impl(circ_id, conn);
if (!circ || circ->marked_for_close)
@@ -444,7 +577,7 @@ circuit_get_by_circid_orconn(uint16_t circ_id, connection_t *conn)
* Return NULL if no such circuit exists.
*/
int
-circuit_id_used_on_conn(uint16_t circ_id, connection_t *conn)
+circuit_id_used_on_conn(uint16_t circ_id, or_connection_t *conn)
{
circuit_t *circ = circuit_get_by_circid_orconn_impl(circ_id, conn);
if (circ && circ->marked_for_close)
@@ -456,13 +589,14 @@ circuit_id_used_on_conn(uint16_t circ_id, connection_t *conn)
/** Return the circuit that a given edge connection is using. */
circuit_t *
-circuit_get_by_edge_conn(connection_t *conn)
+circuit_get_by_edge_conn(edge_connection_t *conn)
{
circuit_t *circ;
- tor_assert(CONN_IS_EDGE(conn));
circ = conn->on_circuit;
- tor_assert(!circ || circ->magic == CIRCUIT_MAGIC);
+ tor_assert(!circ ||
+ (CIRCUIT_IS_ORIGIN(circ) ? circ->magic == ORIGIN_CIRCUIT_MAGIC
+ : circ->magic == OR_CIRCUIT_MAGIC));
return circ;
}
@@ -472,18 +606,24 @@ circuit_get_by_edge_conn(connection_t *conn)
* been marked already.
*/
void
-circuit_unlink_all_from_or_conn(connection_t *conn, int reason)
+circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason)
{
circuit_t *circ;
for (circ = global_circuitlist; circ; circ = circ->next) {
- if (circ->n_conn == conn || circ->p_conn == conn) {
- if (circ->n_conn == conn)
- circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
- if (circ->p_conn == conn)
- circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
- if (!circ->marked_for_close)
- circuit_mark_for_close(circ, reason);
+ int mark = 0;
+ if (circ->n_conn == conn) {
+ circuit_set_n_circid_orconn(circ, 0, NULL);
+ mark = 1;
+ }
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (or_circ->p_conn == conn) {
+ circuit_set_p_circid_orconn(or_circ, 0, NULL);
+ mark = 1;
+ }
}
+ if (mark && !circ->marked_for_close)
+ circuit_mark_for_close(circ, reason);
}
}
@@ -493,62 +633,91 @@ circuit_unlink_all_from_or_conn(connection_t *conn, int reason)
*
* Return NULL if no such circuit exists.
*/
-circuit_t *
+origin_circuit_t *
circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose)
{
circuit_t *circ;
+ tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(purpose));
+
for (circ = global_circuitlist; circ; circ = circ->next) {
if (!circ->marked_for_close &&
circ->purpose == purpose &&
- !rend_cmp_service_ids(rend_query, circ->rend_query))
- return circ;
+ !rend_cmp_service_ids(rend_query, TO_ORIGIN_CIRCUIT(circ)->rend_query))
+ return TO_ORIGIN_CIRCUIT(circ);
}
return NULL;
}
/** Return the first circuit in global_circuitlist after <b>start</b>
- * whose rend_pk_digest field is <b>digest</b> and whose purpose is
- * <b>purpose</b>. Returns NULL if no circuit is found.
+ * whose purpose is <b>purpose</b> is purpose, and (if set) whose
+ * <b>digest</b> matches the rend_pk_digest field. Return NULL if no
+ * circuit is found.
* If <b>start</b> is NULL, begin at the start of the list.
+ * DOCDOC origin.
*/
-circuit_t *
-circuit_get_next_by_pk_and_purpose(circuit_t *start,
+origin_circuit_t *
+circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const char *digest, uint8_t purpose)
{
circuit_t *circ;
+ tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(purpose));
if (start == NULL)
circ = global_circuitlist;
else
- circ = start->next;
+ circ = TO_CIRCUIT(start)->next;
for ( ; circ; circ = circ->next) {
if (circ->marked_for_close)
continue;
if (circ->purpose != purpose)
continue;
- if (!memcmp(circ->rend_pk_digest, digest, DIGEST_LEN))
- return circ;
+ if (!digest)
+ return TO_ORIGIN_CIRCUIT(circ);
+ else if (!memcmp(TO_ORIGIN_CIRCUIT(circ)->rend_pk_digest,
+ digest, DIGEST_LEN))
+ return TO_ORIGIN_CIRCUIT(circ);
}
return NULL;
}
-/** Return the circuit waiting for a rendezvous with the provided cookie.
- * Return NULL if no such circuit is found.
- */
-circuit_t *
-circuit_get_rendezvous(const char *cookie)
+/* DOCDOC */
+static or_circuit_t *
+circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token,
+ size_t len)
{
circuit_t *circ;
for (circ = global_circuitlist; circ; circ = circ->next) {
if (! circ->marked_for_close &&
- circ->purpose == CIRCUIT_PURPOSE_REND_POINT_WAITING &&
- ! memcmp(circ->rend_cookie, cookie, REND_COOKIE_LEN) )
- return circ;
+ circ->purpose == purpose &&
+ ! memcmp(TO_OR_CIRCUIT(circ)->rend_token, token, len))
+ return TO_OR_CIRCUIT(circ);
}
return NULL;
}
+/** Return the circuit waiting for a rendezvous with the provided cookie.
+ * Return NULL if no such circuit is found.
+ */
+or_circuit_t *
+circuit_get_rendezvous(const char *cookie)
+{
+ return circuit_get_by_rend_token_and_purpose(
+ CIRCUIT_PURPOSE_REND_POINT_WAITING,
+ cookie, REND_COOKIE_LEN);
+}
+
+/** Return the circuit waiting for intro cells of the given digest.
+ * Return NULL if no such circuit is found.
+ */
+or_circuit_t *
+circuit_get_intro_point(const char *digest)
+{
+ return circuit_get_by_rend_token_and_purpose(
+ CIRCUIT_PURPOSE_INTRO_POINT, digest,
+ DIGEST_LEN);
+}
+
/** Return a circuit that is open, has specified <b>purpose</b>,
* has a timestamp_dirty value of 0, is uptime/capacity/internal
* if required, and if info is defined, does not already use info
@@ -559,41 +728,43 @@ circuit_get_rendezvous(const char *cookie)
*
* Only return internal circuits if that is requested.
*/
-circuit_t *
+origin_circuit_t *
circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
int need_uptime,
int need_capacity, int internal)
{
- circuit_t *circ;
- circuit_t *best=NULL;
+ circuit_t *_circ;
+ origin_circuit_t *best=NULL;
log_debug(LD_CIRC,
"Hunting for a circ to cannibalize: purpose %d, uptime %d, "
"capacity %d, internal %d",
purpose, need_uptime, need_capacity, internal);
- for (circ=global_circuitlist; circ; circ = circ->next) {
- if (CIRCUIT_IS_ORIGIN(circ) &&
- circ->state == CIRCUIT_STATE_OPEN &&
- !circ->marked_for_close &&
- circ->purpose == purpose &&
- !circ->timestamp_dirty &&
- (!need_uptime || circ->build_state->need_uptime) &&
- (!need_capacity || circ->build_state->need_capacity) &&
- (internal == circ->build_state->is_internal)) {
- if (info) {
- /* need to make sure we don't duplicate hops */
- crypt_path_t *hop = circ->cpath;
- do {
- if (!memcmp(hop->extend_info->identity_digest,
- info->identity_digest, DIGEST_LEN))
- goto next;
- hop=hop->next;
- } while (hop!=circ->cpath);
- }
- if (!best || (best->build_state->need_uptime && !need_uptime))
- best = circ;
+ for (_circ=global_circuitlist; _circ; _circ = _circ->next) {
+ if (CIRCUIT_IS_ORIGIN(_circ) &&
+ _circ->state == CIRCUIT_STATE_OPEN &&
+ !_circ->marked_for_close &&
+ _circ->purpose == purpose &&
+ !_circ->timestamp_dirty) {
+ origin_circuit_t *circ = TO_ORIGIN_CIRCUIT(_circ);
+ if ((!need_uptime || circ->build_state->need_uptime) &&
+ (!need_capacity || circ->build_state->need_capacity) &&
+ (internal == circ->build_state->is_internal)) {
+ if (info) {
+ /* need to make sure we don't duplicate hops */
+ crypt_path_t *hop = circ->cpath;
+ do {
+ if (!memcmp(hop->extend_info->identity_digest,
+ info->identity_digest, DIGEST_LEN))
+ goto next;
+ hop=hop->next;
+ } while (hop!=circ->cpath);
+ }
+ if (!best || (best->build_state->need_uptime && !need_uptime))
+ best = circ;
next: ;
+ }
}
}
return best;
@@ -654,8 +825,6 @@ void
_circuit_mark_for_close(circuit_t *circ, int reason, int line,
const char *file)
{
- connection_t *conn;
-
assert_circuit_ok(circ);
tor_assert(line);
tor_assert(file);
@@ -685,7 +854,7 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line,
}
if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) {
- onion_pending_remove(circ);
+ onion_pending_remove(TO_OR_CIRCUIT(circ));
}
/* If the circuit ever became OPEN, we sent it to the reputation history
* module then. If it isn't OPEN, we send it there now to remember which
@@ -693,60 +862,74 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line,
*/
if (circ->state != CIRCUIT_STATE_OPEN) {
if (CIRCUIT_IS_ORIGIN(circ)) {
- circuit_build_failed(circ); /* take actions if necessary */
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ circuit_build_failed(ocirc); /* take actions if necessary */
+ circuit_rep_hist_note_result(ocirc);
}
- circuit_rep_hist_note_result(circ);
}
if (circ->state == CIRCUIT_STATE_OR_WAIT) {
if (circuits_pending_or_conns)
smartlist_remove(circuits_pending_or_conns, circ);
}
if (CIRCUIT_IS_ORIGIN(circ)) {
- control_event_circuit_status(circ,
+ control_event_circuit_status(TO_ORIGIN_CIRCUIT(circ),
(circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED);
}
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
tor_assert(circ->state == CIRCUIT_STATE_OPEN);
- tor_assert(circ->build_state->chosen_exit);
+ tor_assert(ocirc->build_state->chosen_exit);
/* treat this like getting a nack from it */
log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). "
"Removing from descriptor.",
- safe_str(circ->rend_query),
- safe_str(build_state_get_exit_nickname(circ->build_state)));
- rend_client_remove_intro_point(circ->build_state->chosen_exit,
- circ->rend_query);
+ safe_str(ocirc->rend_query),
+ safe_str(build_state_get_exit_nickname(ocirc->build_state)));
+ rend_client_remove_intro_point(ocirc->build_state->chosen_exit,
+ ocirc->rend_query);
}
-
if (circ->n_conn)
connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason);
- for (conn=circ->n_streams; conn; conn=conn->next_stream)
- connection_edge_destroy(circ->n_circ_id, conn);
- while (circ->resolving_streams) {
- conn = circ->resolving_streams;
- circ->resolving_streams = conn->next_stream;
- if (!conn->marked_for_close) {
- /* The other side will see a DESTROY, and infer that the connections
- * are closing because the circuit is getting torn down. No need
- * to send an end cell. */
- conn->has_sent_end = 1;
- connection_mark_for_close(conn);
+
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ edge_connection_t *conn;
+ for (conn=or_circ->n_streams; conn; conn=conn->next_stream)
+ connection_edge_destroy(or_circ->p_circ_id, conn);
+
+ while (or_circ->resolving_streams) {
+ conn = or_circ->resolving_streams;
+ or_circ->resolving_streams = conn->next_stream;
+ if (!conn->_base.marked_for_close) {
+ /* The other side will see a DESTROY, and infer that the connections
+ * are closing because the circuit is getting torn down. No need
+ * to send an end cell. */
+ conn->_base.edge_has_sent_end = 1;
+ connection_mark_for_close(TO_CONN(conn));
+ }
+ conn->on_circuit = NULL;
}
- conn->on_circuit = NULL;
+
+ if (or_circ->p_conn)
+ connection_or_send_destroy(or_circ->p_circ_id, or_circ->p_conn, reason);
+ } else {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ edge_connection_t *conn;
+ for (conn=ocirc->p_streams; conn; conn=conn->next_stream)
+ connection_edge_destroy(circ->n_circ_id, conn);
}
- if (circ->p_conn)
- connection_or_send_destroy(circ->p_circ_id, circ->p_conn, reason);
- for (conn=circ->p_streams; conn; conn=conn->next_stream)
- connection_edge_destroy(circ->p_circ_id, conn);
circ->marked_for_close = line;
circ->marked_for_close_file = file;
- if (circ->rend_splice) {
- if (!circ->rend_splice->marked_for_close) {
- /* do this after marking this circuit, to avoid infinite recursion. */
- circuit_mark_for_close(circ->rend_splice, reason);
+ if (! CIRCUIT_IS_ORIGIN(circ)) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (or_circ->rend_splice) {
+ if (!or_circ->rend_splice->_base.marked_for_close) {
+ /* do this after marking this circuit, to avoid infinite recursion. */
+ circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason);
+ }
+ or_circ->rend_splice = NULL;
}
- circ->rend_splice = NULL;
}
}
@@ -809,46 +992,49 @@ assert_cpath_ok(const crypt_path_t *cp)
void
assert_circuit_ok(const circuit_t *c)
{
- connection_t *conn;
+ edge_connection_t *conn;
+ const or_circuit_t *or_circ = NULL;
+ const origin_circuit_t *origin_circ = NULL;
tor_assert(c);
- tor_assert(c->magic == CIRCUIT_MAGIC);
+ tor_assert(c->magic == ORIGIN_CIRCUIT_MAGIC || c->magic == OR_CIRCUIT_MAGIC);
tor_assert(c->purpose >= _CIRCUIT_PURPOSE_MIN &&
c->purpose <= _CIRCUIT_PURPOSE_MAX);
+ if (CIRCUIT_IS_ORIGIN(c))
+ origin_circ = TO_ORIGIN_CIRCUIT((circuit_t*)c);
+ else
+ or_circ = TO_OR_CIRCUIT((circuit_t*)c);
+
if (c->n_conn) {
- tor_assert(c->n_conn->type == CONN_TYPE_OR);
tor_assert(!memcmp(c->n_conn->identity_digest, c->n_conn_id_digest,
DIGEST_LEN));
if (c->n_circ_id)
tor_assert(c == circuit_get_by_circid_orconn(c->n_circ_id, c->n_conn));
}
- if (c->p_conn) {
- tor_assert(c->p_conn->type == CONN_TYPE_OR);
- if (c->p_circ_id)
- tor_assert(c == circuit_get_by_circid_orconn(c->p_circ_id, c->p_conn));
+ if (or_circ && or_circ->p_conn) {
+ if (or_circ->p_circ_id)
+ tor_assert(c == circuit_get_by_circid_orconn(or_circ->p_circ_id,
+ or_circ->p_conn));
}
- for (conn = c->p_streams; conn; conn = conn->next_stream)
- tor_assert(conn->type == CONN_TYPE_AP);
- for (conn = c->n_streams; conn; conn = conn->next_stream)
- tor_assert(conn->type == CONN_TYPE_EXIT);
+#if 0 /* false now that rendezvous exits are attached to p_streams */
+ if (origin_circ)
+ for (conn = origin_circ->p_streams; conn; conn = conn->next_stream)
+ tor_assert(conn->_base.type == CONN_TYPE_AP);
+#endif
+ if (or_circ)
+ for (conn = or_circ->n_streams; conn; conn = conn->next_stream)
+ tor_assert(conn->_base.type == CONN_TYPE_EXIT);
tor_assert(c->deliver_window >= 0);
tor_assert(c->package_window >= 0);
if (c->state == CIRCUIT_STATE_OPEN) {
tor_assert(!c->onionskin);
- if (c->cpath) {
- tor_assert(CIRCUIT_IS_ORIGIN(c));
- tor_assert(!c->n_crypto);
- tor_assert(!c->p_crypto);
- tor_assert(!c->n_digest);
- tor_assert(!c->p_digest);
- } else {
- tor_assert(!CIRCUIT_IS_ORIGIN(c));
- tor_assert(c->n_crypto);
- tor_assert(c->p_crypto);
- tor_assert(c->n_digest);
- tor_assert(c->p_digest);
+ if (or_circ) {
+ tor_assert(or_circ->n_crypto);
+ tor_assert(or_circ->p_crypto);
+ tor_assert(or_circ->n_digest);
+ tor_assert(or_circ->p_digest);
}
}
if (c->state == CIRCUIT_STATE_OR_WAIT && !c->marked_for_close) {
@@ -858,17 +1044,18 @@ assert_circuit_ok(const circuit_t *c)
tor_assert(!circuits_pending_or_conns ||
!smartlist_isin(circuits_pending_or_conns, c));
}
- if (c->cpath) {
- assert_cpath_ok(c->cpath);
+ if (origin_circ && origin_circ->cpath) {
+ assert_cpath_ok(origin_circ->cpath);
}
if (c->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED) {
+ tor_assert(or_circ);
if (!c->marked_for_close) {
- tor_assert(c->rend_splice);
- tor_assert(c->rend_splice->rend_splice == c);
+ tor_assert(or_circ->rend_splice);
+ tor_assert(or_circ->rend_splice->rend_splice == or_circ);
}
- tor_assert(c->rend_splice != c);
+ tor_assert(or_circ->rend_splice != or_circ);
} else {
- tor_assert(!c->rend_splice);
+ tor_assert(!or_circ || !or_circ->rend_splice);
}
}
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 2aec912c17..310e8278fb 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -26,12 +26,13 @@ static void circuit_increment_failure_count(void);
* Else return 0.
*/
static int
-circuit_is_acceptable(circuit_t *circ, connection_t *conn,
+circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
int must_be_open, uint8_t purpose,
int need_uptime, int need_internal,
time_t now)
{
routerinfo_t *exitrouter;
+ cpath_build_state_t *build_state;
tor_assert(circ);
tor_assert(conn);
tor_assert(conn->socks_request);
@@ -71,11 +72,12 @@ circuit_is_acceptable(circuit_t *circ, connection_t *conn,
* circuit, it's the magical extra bob hop. so just check the nickname
* of the one we meant to finish at.
*/
- exitrouter = build_state_get_exit_router(circ->build_state);
+ build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
+ exitrouter = build_state_get_exit_router(build_state);
- if (need_uptime && !circ->build_state->need_uptime)
+ if (need_uptime && !build_state->need_uptime)
return 0;
- if (need_internal != circ->build_state->is_internal)
+ if (need_internal != build_state->is_internal)
return 0;
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
@@ -89,7 +91,8 @@ circuit_is_acceptable(circuit_t *circ, connection_t *conn,
return 0;
}
} else { /* not general */
- if (rend_cmp_service_ids(conn->rend_query, circ->rend_query)) {
+ if (rend_cmp_service_ids(conn->rend_query,
+ TO_ORIGIN_CIRCUIT(circ)->rend_query)) {
/* this circ is not for this conn */
return 0;
}
@@ -114,9 +117,11 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
return 1;
} else {
if (a->timestamp_dirty ||
- b->build_state->is_internal ||
a->timestamp_created > b->timestamp_created)
return 1;
+ if (CIRCUIT_IS_ORIGIN(b) &&
+ TO_ORIGIN_CIRCUIT(b)->build_state->is_internal)
+ return 1;
}
break;
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
@@ -149,8 +154,8 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
* If it's INTRODUCE_ACK_WAIT and must_be_open==0, then return the
* closest introduce-purposed circuit that you can find.
*/
-static circuit_t *
-circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose,
+static origin_circuit_t *
+circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
int need_uptime, int need_internal)
{
circuit_t *circ, *best=NULL;
@@ -163,6 +168,8 @@ circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose,
purpose == CIRCUIT_PURPOSE_C_REND_JOINED);
for (circ=global_circuitlist;circ;circ = circ->next) {
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,
need_uptime,need_internal,now))
continue;
@@ -174,7 +181,7 @@ circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose,
best = circ;
}
- return best;
+ return best ? TO_ORIGIN_CIRCUIT(best) : NULL;
}
/** Close all circuits that start at us, aren't open, and were born
@@ -230,7 +237,8 @@ circuit_expire_building(time_t now)
/* c_rend_ready circs measure age since timestamp_dirty,
* because that's set when they switch purposes
*/
- if (!victim->rend_query[0] || victim->timestamp_dirty > cutoff)
+ if (TO_ORIGIN_CIRCUIT(victim)->rend_query[0] ||
+ victim->timestamp_dirty > cutoff)
continue;
break;
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
@@ -247,7 +255,8 @@ circuit_expire_building(time_t now)
if (victim->n_conn)
log_info(LD_CIRC,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)",
- victim->n_conn->address, victim->n_port, victim->n_circ_id,
+ victim->n_conn->_base.address, victim->n_port,
+ victim->n_circ_id,
victim->state, circuit_state_to_string(victim->state),
victim->purpose);
else
@@ -255,7 +264,7 @@ circuit_expire_building(time_t now)
victim->n_circ_id, victim->state,
circuit_state_to_string(victim->state), victim->purpose);
- circuit_log_path(LOG_INFO,LD_CIRC,victim);
+ circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim));
circuit_mark_for_close(victim, END_CIRC_AT_ORIGIN);
}
}
@@ -288,7 +297,8 @@ circuit_remove_handled_ports(smartlist_t *needed_ports)
* Else return 0.
*/
int
-circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
+circuit_stream_is_being_handled(edge_connection_t *conn,
+ uint16_t port, int min)
{
circuit_t *circ;
routerinfo_t *exitrouter;
@@ -301,12 +311,14 @@ circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
if (CIRCUIT_IS_ORIGIN(circ) &&
!circ->marked_for_close &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
- !circ->build_state->is_internal &&
(!circ->timestamp_dirty ||
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness > now)) {
- exitrouter = build_state_get_exit_router(circ->build_state);
- if (exitrouter &&
- (!need_uptime || circ->build_state->need_uptime)) {
+ cpath_build_state_t *build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
+ if (build_state->is_internal)
+ continue;
+
+ exitrouter = build_state_get_exit_router(build_state);
+ if (exitrouter && (!need_uptime || build_state->need_uptime)) {
int ok;
if (conn) {
ok = connection_ap_can_use_exit(conn, exitrouter);
@@ -343,6 +355,7 @@ circuit_predict_and_launch_new(void)
/* First, count how many of each type of circuit we have already. */
for (circ=global_circuitlist;circ;circ = circ->next) {
+ cpath_build_state_t *build_state;
if (!CIRCUIT_IS_ORIGIN(circ))
continue;
if (circ->marked_for_close)
@@ -352,9 +365,10 @@ circuit_predict_and_launch_new(void)
if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL)
continue; /* only pay attention to general-purpose circs */
num++;
- if (circ->build_state->is_internal)
+ build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
+ if (build_state->is_internal)
num_internal++;
- if (circ->build_state->need_uptime && circ->build_state->is_internal)
+ if (build_state->need_uptime && build_state->is_internal)
num_uptime_internal++;
}
@@ -445,9 +459,9 @@ circuit_build_needed_circs(time_t now)
* lists of <b>circ</b>, then remove it from the list.
*/
void
-circuit_detach_stream(circuit_t *circ, connection_t *conn)
+circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
{
- connection_t *prevconn;
+ edge_connection_t *prevconn;
tor_assert(circ);
tor_assert(conn);
@@ -455,44 +469,49 @@ circuit_detach_stream(circuit_t *circ, connection_t *conn)
conn->cpath_layer = NULL; /* make sure we don't keep a stale pointer */
conn->on_circuit = NULL;
- if (conn == circ->p_streams) {
- circ->p_streams = conn->next_stream;
- return;
- }
- if (conn == circ->n_streams) {
- circ->n_streams = conn->next_stream;
- return;
- }
- if (conn == circ->resolving_streams) {
- circ->resolving_streams = conn->next_stream;
- return;
- }
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ if (conn == origin_circ->p_streams) {
+ origin_circ->p_streams = conn->next_stream;
+ return;
+ }
- for (prevconn = circ->p_streams;
- prevconn && prevconn->next_stream && prevconn->next_stream != conn;
- prevconn = prevconn->next_stream)
- ;
- if (prevconn && prevconn->next_stream) {
- prevconn->next_stream = conn->next_stream;
- return;
- }
+ for (prevconn = origin_circ->p_streams;
+ prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+ prevconn = prevconn->next_stream)
+ ;
+ if (prevconn && prevconn->next_stream) {
+ prevconn->next_stream = conn->next_stream;
+ return;
+ }
+ } else {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
+ if (conn == or_circ->n_streams) {
+ or_circ->n_streams = conn->next_stream;
+ return;
+ }
+ if (conn == or_circ->resolving_streams) {
+ or_circ->resolving_streams = conn->next_stream;
+ return;
+ }
- for (prevconn = circ->n_streams;
- prevconn && prevconn->next_stream && prevconn->next_stream != conn;
- prevconn = prevconn->next_stream)
- ;
- if (prevconn && prevconn->next_stream) {
- prevconn->next_stream = conn->next_stream;
- return;
- }
+ for (prevconn = or_circ->n_streams;
+ prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+ prevconn = prevconn->next_stream)
+ ;
+ if (prevconn && prevconn->next_stream) {
+ prevconn->next_stream = conn->next_stream;
+ return;
+ }
- for (prevconn = circ->resolving_streams;
- prevconn && prevconn->next_stream && prevconn->next_stream != conn;
- prevconn = prevconn->next_stream)
- ;
- if (prevconn && prevconn->next_stream) {
- prevconn->next_stream = conn->next_stream;
- return;
+ for (prevconn = or_circ->resolving_streams;
+ prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+ prevconn = prevconn->next_stream)
+ ;
+ if (prevconn && prevconn->next_stream) {
+ prevconn->next_stream = conn->next_stream;
+ return;
+ }
}
log_err(LD_BUG,"edge conn not in circuit's list?");
@@ -519,10 +538,11 @@ circuit_about_to_close_connection(connection_t *conn)
if (!connection_state_is_open(conn)) {
/* Inform any pending (not attached) circs that they should
* give up. */
- circuit_n_conn_done(conn, 0);
+ circuit_n_conn_done(TO_OR_CONN(conn), 0);
}
/* Now close all the attached circuits on it. */
- circuit_unlink_all_from_or_conn(conn, END_CIRC_REASON_OR_CONN_CLOSED);
+ circuit_unlink_all_from_or_conn(TO_OR_CONN(conn),
+ END_CIRC_REASON_OR_CONN_CLOSED);
return;
}
case CONN_TYPE_AP:
@@ -533,17 +553,17 @@ circuit_about_to_close_connection(connection_t *conn)
* been sent. But don't kill the circuit.
*/
- circ = circuit_get_by_edge_conn(conn);
+ circ = circuit_get_by_edge_conn(TO_EDGE_CONN(conn));
if (!circ)
return;
- circuit_detach_stream(circ, conn);
+ circuit_detach_stream(circ, TO_EDGE_CONN(conn));
}
} /* end switch */
}
-/** Find each circuit that has been dirty for too long, and has
- * no streams on it: mark it for close.
+/** Find each circuit that has been unused for too long, or dirty
+ * for too long and has no streax=ms on it: mark it for close.
*/
static void
circuit_expire_old_circuits(time_t now)
@@ -552,23 +572,19 @@ circuit_expire_old_circuits(time_t now)
time_t cutoff = now - get_options()->CircuitIdleTimeout;
for (circ = global_circuitlist; circ; circ = circ->next) {
- if (circ->marked_for_close)
+ if (circ->marked_for_close || ! CIRCUIT_IS_ORIGIN(circ))
continue;
/* If the circuit has been dirty for too long, and there are no streams
* on it, mark it for close.
*/
if (circ->timestamp_dirty &&
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now &&
- CIRCUIT_IS_ORIGIN(circ) &&
- !circ->p_streams /* nothing attached */ ) {
+ !TO_ORIGIN_CIRCUIT(circ)->p_streams /* nothing attached */ ) {
log_debug(LD_CIRC, "Closing n_circ_id %d (dirty %d secs ago, purp %d)",
circ->n_circ_id, (int)(now - circ->timestamp_dirty),
circ->purpose);
- /* (only general and purpose_c circs can get dirty) */
- tor_assert(!circ->n_streams);
- tor_assert(circ->purpose <= CIRCUIT_PURPOSE_C_REND_JOINED);
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
- } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) &&
+ } else if (!circ->timestamp_dirty &&
circ->state == CIRCUIT_STATE_OPEN &&
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (circ->timestamp_created < cutoff) {
@@ -581,30 +597,72 @@ circuit_expire_old_circuits(time_t now)
}
}
-/** A testing circuit has completed. Take whatever stats we want. */
+#define NUM_PARALLEL_TESTING_CIRCS 4
+
+static int have_performed_bandwidth_test = 0;
+
+/** Reset have_performed_bandwidth_test, so we'll start building
+ * testing circuits again so we can exercise our bandwidth. */
+void
+reset_bandwidth_test(void)
+{
+ have_performed_bandwidth_test = 0;
+}
+
+/** Return 1 if we've already exercised our bandwidth, or if we
+ * have fewer than NUM_PARALLEL_TESTING_CIRCS testing circuits
+ * established or on the way. Else return 0.
+ */
+int
+circuit_enough_testing_circs(void)
+{
+ circuit_t *circ;
+ int num = 0;
+
+ if (have_performed_bandwidth_test)
+ return 1;
+
+ for (circ = global_circuitlist; circ; circ = circ->next) {
+ if (!circ->marked_for_close && CIRCUIT_IS_ORIGIN(circ) &&
+ circ->purpose == CIRCUIT_PURPOSE_TESTING &&
+ circ->state == CIRCUIT_STATE_OPEN)
+ num++;
+ }
+ return num >= NUM_PARALLEL_TESTING_CIRCS;
+}
+
+/** A testing circuit has completed. Take whatever stats we want.
+ * Noticing reachability is taken care of in onionskin_answer(),
+ * so there's no need to record anything here. But if we still want
+ * to do the bandwidth test, and we now have enough testing circuits
+ * open, do it.
+ */
static void
-circuit_testing_opened(circuit_t *circ)
+circuit_testing_opened(origin_circuit_t *circ)
{
- /* For now, we only use testing circuits to see if our ORPort is
- reachable. But we remember reachability in onionskin_answer(),
- so there's no need to record anything here. Just close the circ. */
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ if (have_performed_bandwidth_test) {
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
+ } else if (circuit_enough_testing_circs()) {
+ router_perform_bandwidth_test(NUM_PARALLEL_TESTING_CIRCS, time(NULL));
+ have_performed_bandwidth_test = 1;
+ } else
+ consider_testing_reachability(1, 0);
}
/** A testing circuit has failed to build. Take whatever stats we want. */
static void
-circuit_testing_failed(circuit_t *circ, int at_last_hop)
+circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
{
-#if 0
- routerinfo_t *me = router_get_my_routerinfo();
+ if (server_mode(get_options()) && check_whether_orport_reachable())
+ return;
- if (!at_last_hop)
- circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me, 0, 1, 1);
- else
-#endif
log_info(LD_GENERAL,
"Our testing circuit (to see if your ORPort is reachable) "
"has failed. I'll try again later.");
+
+ /* These aren't used yet. */
+ (void)circ;
+ (void)at_last_hop;
}
/** The circuit <b>circ</b> has just become open. Take the next
@@ -614,11 +672,11 @@ circuit_testing_failed(circuit_t *circ, int at_last_hop)
* that could use circ.
*/
void
-circuit_has_opened(circuit_t *circ)
+circuit_has_opened(origin_circuit_t *circ)
{
control_event_circuit_status(circ, CIRC_EVENT_BUILT);
- switch (circ->purpose) {
+ switch (TO_CIRCUIT(circ)->purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
connection_ap_attach_pending();
@@ -643,7 +701,7 @@ circuit_has_opened(circuit_t *circ)
circuit_testing_opened(circ);
break;
default:
- log_err(LD_BUG,"unhandled purpose %d",circ->purpose);
+ log_err(LD_BUG,"unhandled purpose %d",circ->_base.purpose);
tor_assert(0);
}
}
@@ -651,7 +709,7 @@ circuit_has_opened(circuit_t *circ)
/** Called whenever a circuit could not be successfully built.
*/
void
-circuit_build_failed(circuit_t *circ)
+circuit_build_failed(origin_circuit_t *circ)
{
/* we should examine circ and see if it failed because of
* the last hop or an earlier hop. then use this info below.
@@ -668,38 +726,35 @@ circuit_build_failed(circuit_t *circ)
circ->cpath->state != CPATH_STATE_OPEN) {
/* We failed at the first hop. If there's an OR connection
to blame, blame it. */
- connection_t *n_conn = NULL;
- if (circ->n_conn) {
- n_conn = circ->n_conn;
- } else if (circ->state == CIRCUIT_STATE_OR_WAIT) {
+ or_connection_t *n_conn = NULL;
+ if (circ->_base.n_conn) {
+ n_conn = circ->_base.n_conn;
+ } else if (circ->_base.state == CIRCUIT_STATE_OR_WAIT) {
/* we have to hunt for it */
- n_conn = connection_or_get_by_identity_digest(circ->n_conn_id_digest);
+ n_conn = connection_or_get_by_identity_digest(
+ circ->_base.n_conn_id_digest);
}
if (n_conn) {
log_info(LD_OR,
"Our circuit failed to get a response from the first hop "
"(%s:%d). I'm going to try to rotate to a better connection.",
- n_conn->address, n_conn->port);
- n_conn->is_obsolete = 1;
+ n_conn->_base.address, n_conn->_base.port);
+ n_conn->_base.or_is_obsolete = 1;
entry_guard_set_status(n_conn->identity_digest, 0);
}
}
- switch (circ->purpose) {
+ switch (circ->_base.purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
- if (circ->state != CIRCUIT_STATE_OPEN) {
- /* If we never built the circuit, note it as a failure. */
- /* Note that we can't just check circ->cpath here, because if
- * circuit-building failed immediately, it won't be set yet. */
- circuit_increment_failure_count();
- }
+ /* If we never built the circuit, note it as a failure. */
+ circuit_increment_failure_count();
break;
case CIRCUIT_PURPOSE_TESTING:
circuit_testing_failed(circ, failed_at_last_hop);
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* at Bob, waiting for introductions */
- if (circ->state != CIRCUIT_STATE_OPEN) {
+ if (circ->_base.state != CIRCUIT_STATE_OPEN) {
circuit_increment_failure_count();
}
/* no need to care here, because bob will rebuild intro
@@ -714,9 +769,7 @@ circuit_build_failed(circuit_t *circ)
break;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
/* at Alice, waiting for Bob */
- if (circ->state != CIRCUIT_STATE_OPEN) {
- circuit_increment_failure_count();
- }
+ circuit_increment_failure_count();
/* Alice will pick a new rend point when this one dies, if
* the stream in question still cares. No need to act here. */
break;
@@ -750,11 +803,11 @@ static int did_circs_fail_last_period = 0;
/** Launch a new circuit; see circuit_launch_by_extend_info() for
* details on arguments. */
-circuit_t *
+origin_circuit_t *
circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
int need_uptime, int need_capacity, int internal)
{
- circuit_t *circ;
+ origin_circuit_t *circ;
extend_info_t *info = NULL;
if (exit)
info = extend_info_from_router(exit);
@@ -771,11 +824,11 @@ circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
* choose among routers with high bandwidth. If <b>internal</b> is true, the
* last hop need not be an exit node. Return the newly allocated circuit on
* success, or NULL on failure. */
-circuit_t *
+origin_circuit_t *
circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *extend_info,
int need_uptime, int need_capacity, int internal)
{
- circuit_t *circ;
+ origin_circuit_t *circ;
if (!router_have_minimum_dir_info()) {
log_debug(LD_CIRC,"Haven't fetched enough directory info yet; canceling "
@@ -791,11 +844,11 @@ circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *extend_info,
if (circ) {
log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d",
build_state_get_exit_nickname(circ->build_state), purpose);
- circ->purpose = purpose;
+ circ->_base.purpose = purpose;
/* reset the birth date of this circ, else expire_building
* will see it and think it's been trying to build since it
* began. */
- circ->timestamp_created = time(NULL);
+ circ->_base.timestamp_created = time(NULL);
switch (purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
@@ -835,7 +888,7 @@ circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *extend_info,
/** Launch a new circuit; see circuit_launch_by_extend_info() for
* details on arguments. */
-circuit_t *
+origin_circuit_t *
circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
int need_uptime, int need_capacity, int internal)
{
@@ -876,25 +929,26 @@ circuit_reset_failure_count(int timeout)
n_circuit_failures = 0;
}
-/** Find an open circ that we're happy with: return 1. If there isn't
- * one, and there isn't one on the way, launch one and return 0. If it
- * will never work, return -1.
+/** Find an open circ that we're happy to use for <b>conn</b> and return 1. If
+ * there isn't one, and there isn't one on the way, launch one and return
+ * 0. If it will never work, return -1.
*
* Write the found or in-progress or launched circ into *circp.
*/
static int
-circuit_get_open_circ_or_launch(connection_t *conn,
+circuit_get_open_circ_or_launch(edge_connection_t *conn,
uint8_t desired_circuit_purpose,
- circuit_t **circp)
+ origin_circuit_t **circp)
{
- circuit_t *circ;
+ origin_circuit_t *circ;
int is_resolve;
int need_uptime, need_internal;
tor_assert(conn);
tor_assert(circp);
- tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
- is_resolve = conn->socks_request->command == SOCKS_COMMAND_RESOLVE;
+ tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ is_resolve = (conn->socks_request->command == SOCKS_COMMAND_RESOLVE ||
+ conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR);
need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
conn->socks_request->port);
@@ -956,7 +1010,7 @@ circuit_get_open_circ_or_launch(connection_t *conn,
"No intro points for '%s': refetching service descriptor.",
safe_str(conn->rend_query));
rend_client_refetch_renddesc(conn->rend_query);
- conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
log_info(LD_REND,"Chose '%s' as intro point for '%s'.",
@@ -969,11 +1023,16 @@ circuit_get_open_circ_or_launch(connection_t *conn,
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (conn->chosen_exit_name) {
routerinfo_t *r;
+ int opt = conn->_base.chosen_exit_optional;
if (!(r = router_get_by_nickname(conn->chosen_exit_name, 1))) {
- /*XXXX NM domain? */
- log_notice(LD_CIRC,
- "Requested exit point '%s' is not known. Closing.",
- conn->chosen_exit_name);
+ log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
+ "Requested exit point '%s' is not known. %s.",
+ conn->chosen_exit_name, opt ? "Trying others" : "Closing");
+ if (opt) {
+ conn->_base.chosen_exit_optional = 0;
+ tor_free(conn->chosen_exit_name);
+ return 0;
+ }
return -1;
}
extend_info = extend_info_from_router(r);
@@ -998,8 +1057,8 @@ circuit_get_open_circ_or_launch(connection_t *conn,
if (circ) {
/* write the service_id into circ */
strlcpy(circ->rend_query, conn->rend_query, sizeof(circ->rend_query));
- if (circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
- circ->state == CIRCUIT_STATE_OPEN)
+ if (circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
+ circ->_base.state == CIRCUIT_STATE_OPEN)
rend_client_rendcirc_has_opened(circ);
}
}
@@ -1018,19 +1077,18 @@ circuit_get_open_circ_or_launch(connection_t *conn,
* circ's cpath.
*/
static void
-link_apconn_to_circ(connection_t *apconn, circuit_t *circ)
+link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ)
{
/* add it into the linked list of streams on this circuit */
log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %d.",
- circ->n_circ_id);
+ circ->_base.n_circ_id);
/* reset it, so we can measure circ timeouts */
- apconn->timestamp_lastread = time(NULL);
+ apconn->_base.timestamp_lastread = time(NULL);
apconn->next_stream = circ->p_streams;
- apconn->on_circuit = circ;
+ apconn->on_circuit = TO_CIRCUIT(circ);
/* assert_connection_ok(conn, time(NULL)); */
circ->p_streams = apconn;
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
tor_assert(circ->cpath);
tor_assert(circ->cpath->prev);
tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
@@ -1040,7 +1098,7 @@ link_apconn_to_circ(connection_t *apconn, circuit_t *circ)
/** If an exit wasn't specifically chosen, save the history for future
* use. */
static void
-consider_recording_trackhost(connection_t *conn, circuit_t *circ)
+consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ)
{
int found_needle = 0;
char *str;
@@ -1052,7 +1110,7 @@ consider_recording_trackhost(connection_t *conn, circuit_t *circ)
/* Search the addressmap for this conn's destination. */
/* If he's not in the address map.. */
if (!options->TrackHostExits ||
- addressmap_already_mapped(conn->socks_request->address))
+ addressmap_have_mapping(conn->socks_request->address))
return; /* nothing to track, or already mapped */
SMARTLIST_FOREACH(options->TrackHostExits, const char *, cp, {
@@ -1094,21 +1152,20 @@ consider_recording_trackhost(connection_t *conn, circuit_t *circ)
* send a begin or resolve cell as appropriate. Return values are as
* for connection_ap_handshake_attach_circuit. */
int
-connection_ap_handshake_attach_chosen_circuit(connection_t *conn,
- circuit_t *circ)
+connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
+ origin_circuit_t *circ)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_AP);
- tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT ||
- conn->state == AP_CONN_STATE_CONTROLLER_WAIT);
+ tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT ||
+ conn->_base.state == AP_CONN_STATE_CONTROLLER_WAIT);
tor_assert(conn->socks_request);
tor_assert(circ);
- tor_assert(circ->state == CIRCUIT_STATE_OPEN);
+ tor_assert(circ->_base.state == CIRCUIT_STATE_OPEN);
- conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
- if (!circ->timestamp_dirty)
- circ->timestamp_dirty = time(NULL);
+ if (!circ->_base.timestamp_dirty)
+ circ->_base.timestamp_dirty = time(NULL);
link_apconn_to_circ(conn, circ);
tor_assert(conn->socks_request);
@@ -1132,19 +1189,18 @@ connection_ap_handshake_attach_chosen_circuit(connection_t *conn,
* right next step, and return 1.
*/
int
-connection_ap_handshake_attach_circuit(connection_t *conn)
+connection_ap_handshake_attach_circuit(edge_connection_t *conn)
{
int retval;
int conn_age;
int severity;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_AP);
- tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(conn->socks_request);
- conn_age = time(NULL) - conn->timestamp_created;
- severity = (!conn->addr && !conn->port) ? LOG_INFO : LOG_NOTICE;
+ conn_age = time(NULL) - conn->_base.timestamp_created;
+ severity = (!conn->_base.addr && !conn->_base.port) ? LOG_INFO : LOG_NOTICE;
if (conn_age > get_options()->SocksTimeout) {
log_fn(severity, LD_APP,
"Tried for %d seconds to get a connection to %s:%d. Giving up.",
@@ -1154,20 +1210,32 @@ connection_ap_handshake_attach_circuit(connection_t *conn)
}
if (!connection_edge_is_rendezvous_stream(conn)) { /* we're a general conn */
- circuit_t *circ=NULL;
+ origin_circuit_t *circ=NULL;
if (conn->chosen_exit_name) {
routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1);
+ int opt = conn->_base.chosen_exit_optional;
if (!router) {
- log_warn(LD_APP,
- "Requested exit point '%s' is not known. Closing.",
- conn->chosen_exit_name);
+ log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
+ "Requested exit point '%s' is not known. %s.",
+ conn->chosen_exit_name, opt ? "Trying others" : "Closing");
+ if (opt) {
+ conn->_base.chosen_exit_optional = 0;
+ tor_free(conn->chosen_exit_name);
+ return 0;
+ }
return -1;
}
- if (!connection_ap_can_use_exit(conn, router)) {
- log_warn(LD_APP,
- "Requested exit point '%s' would refuse request. Closing.",
- conn->chosen_exit_name);
+ if (conn->_base.purpose != EXIT_PURPOSE_RESOLVE &&
+ !connection_ap_can_use_exit(conn, router)) {
+ log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
+ "Requested exit point '%s' would refuse request. %s.",
+ conn->chosen_exit_name, opt ? "Trying others" : "Closing");
+ if (opt) {
+ conn->_base.chosen_exit_optional = 0;
+ tor_free(conn->chosen_exit_name);
+ return 0;
+ }
return -1;
}
}
@@ -1180,7 +1248,7 @@ connection_ap_handshake_attach_circuit(connection_t *conn)
log_debug(LD_APP|LD_CIRC,
"Attaching apconn to circ %d (stream %d sec old).",
- circ->n_circ_id, conn_age);
+ circ->_base.n_circ_id, conn_age);
/* here, print the circ's path. so people can figure out which circs are
* sucking. */
circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ);
@@ -1189,7 +1257,7 @@ connection_ap_handshake_attach_circuit(connection_t *conn)
return connection_ap_handshake_attach_chosen_circuit(conn, circ);
} else { /* we're a rendezvous conn */
- circuit_t *rendcirc=NULL, *introcirc=NULL;
+ origin_circuit_t *rendcirc=NULL, *introcirc=NULL;
tor_assert(!conn->cpath_layer);
@@ -1205,25 +1273,25 @@ connection_ap_handshake_attach_circuit(connection_t *conn)
log_info(LD_REND,
"rend joined circ %d already here. attaching. "
"(stream %d sec old)",
- rendcirc->n_circ_id, conn_age);
+ rendcirc->_base.n_circ_id, conn_age);
/* Mark rendezvous circuits as 'newly dirty' every time you use
* them, since the process of rebuilding a rendezvous circ is so
* expensive. There is a tradeoffs between linkability and
* feasibility, at this point.
*/
- rendcirc->timestamp_dirty = time(NULL);
+ rendcirc->_base.timestamp_dirty = time(NULL);
link_apconn_to_circ(conn, rendcirc);
if (connection_ap_handshake_send_begin(conn, rendcirc) < 0)
return 0; /* already marked, let them fade away */
return 1;
}
- if (rendcirc && (rendcirc->purpose ==
+ if (rendcirc && (rendcirc->_base.purpose ==
CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)) {
log_info(LD_REND,
"pending-join circ %d already here, with intro ack. "
"Stalling. (stream %d sec old)",
- rendcirc->n_circ_id, conn_age);
+ rendcirc->_base.n_circ_id, conn_age);
return 0;
}
@@ -1237,7 +1305,8 @@ connection_ap_handshake_attach_circuit(connection_t *conn)
tor_assert(introcirc);
log_info(LD_REND, "Intro circ %d present and awaiting ack (rend %d). "
"Stalling. (stream %d sec old)",
- introcirc->n_circ_id, rendcirc ? rendcirc->n_circ_id : 0,
+ introcirc->_base.n_circ_id,
+ rendcirc ? rendcirc->_base.n_circ_id : 0,
conn_age);
return 0;
}
@@ -1245,32 +1314,34 @@ connection_ap_handshake_attach_circuit(connection_t *conn)
/* now rendcirc and introcirc are each either undefined or not finished */
if (rendcirc && introcirc &&
- rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY) {
+ rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY) {
log_info(LD_REND,
"ready rend circ %d already here (no intro-ack yet on "
"intro %d). (stream %d sec old)",
- rendcirc->n_circ_id, introcirc->n_circ_id, conn_age);
+ rendcirc->_base.n_circ_id,
+ introcirc->_base.n_circ_id, conn_age);
- tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
- if (introcirc->state == CIRCUIT_STATE_OPEN) {
+ tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+ if (introcirc->_base.state == CIRCUIT_STATE_OPEN) {
log_info(LD_REND,"found open intro circ %d (rend %d); sending "
"introduction. (stream %d sec old)",
- introcirc->n_circ_id, rendcirc->n_circ_id, conn_age);
+ introcirc->_base.n_circ_id, rendcirc->_base.n_circ_id,
+ conn_age);
if (rend_client_send_introduction(introcirc, rendcirc) < 0) {
return -1;
}
- rendcirc->timestamp_dirty = time(NULL);
- introcirc->timestamp_dirty = time(NULL);
- assert_circuit_ok(rendcirc);
- assert_circuit_ok(introcirc);
+ rendcirc->_base.timestamp_dirty = time(NULL);
+ introcirc->_base.timestamp_dirty = time(NULL);
+ assert_circuit_ok(TO_CIRCUIT(rendcirc));
+ assert_circuit_ok(TO_CIRCUIT(introcirc));
return 0;
}
}
log_info(LD_REND, "Intro (%d) and rend (%d) circs are not both ready. "
"Stalling conn. (%d sec old)",
- introcirc ? introcirc->n_circ_id : 0,
- rendcirc ? rendcirc->n_circ_id : 0, conn_age);
+ introcirc ? introcirc->_base.n_circ_id : 0,
+ rendcirc ? rendcirc->_base.n_circ_id : 0, conn_age);
return 0;
}
}
diff --git a/src/or/command.c b/src/or/command.c
index 6c55174772..f87b2b0554 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -27,10 +27,10 @@ uint64_t stats_n_relay_cells_processed = 0;
uint64_t stats_n_destroy_cells_processed = 0;
/* These are the main four functions for processing cells */
-static void command_process_create_cell(cell_t *cell, connection_t *conn);
-static void command_process_created_cell(cell_t *cell, connection_t *conn);
-static void command_process_relay_cell(cell_t *cell, connection_t *conn);
-static void command_process_destroy_cell(cell_t *cell, connection_t *conn);
+static void command_process_create_cell(cell_t *cell, or_connection_t *conn);
+static void command_process_created_cell(cell_t *cell, or_connection_t *conn);
+static void command_process_relay_cell(cell_t *cell, or_connection_t *conn);
+static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn);
#ifdef KEEP_TIMING_STATS
/** This is a wrapper function around the actual function that processes the
@@ -38,8 +38,8 @@ static void command_process_destroy_cell(cell_t *cell, connection_t *conn);
* by the number of microseconds used by the call to <b>*func(cell, conn)</b>.
*/
static void
-command_time_process_cell(cell_t *cell, connection_t *conn, int *time,
- void (*func)(cell_t *, connection_t *))
+command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time,
+ void (*func)(cell_t *, or_connection_t *))
{
struct timeval start, end;
long time_passed;
@@ -68,7 +68,7 @@ command_time_process_cell(cell_t *cell, connection_t *conn, int *time,
* process each type of cell.
*/
void
-command_process_cell(cell_t *cell, connection_t *conn)
+command_process_cell(cell_t *cell, or_connection_t *conn)
{
#ifdef KEEP_TIMING_STATS
/* how many of each cell have we seen so far this second? needs better
@@ -159,9 +159,9 @@ command_process_cell(cell_t *cell, connection_t *conn)
* picked up again when the cpuworker finishes decrypting it.
*/
static void
-command_process_create_cell(cell_t *cell, connection_t *conn)
+command_process_create_cell(cell_t *cell, or_connection_t *conn)
{
- circuit_t *circ;
+ or_circuit_t *circ;
int id_is_high;
if (we_are_hibernating()) {
@@ -182,33 +182,35 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
return;
}
- /* If the high bit of the circuit ID is not as expected, then switch
- * which half of the space we'll use for our own CREATE cells.
- *
- * This can happen because Tor 0.0.9pre5 and earlier decide which
- * half to use based on nickname, and we now use identity keys.
- */
- id_is_high = cell->circ_id & (1<<15);
- if (id_is_high && conn->circ_id_type == CIRC_ID_TYPE_HIGHER) {
- log_info(LD_OR, "Got a high circuit ID from %s (%d); switching to "
- "low circuit IDs.",
- conn->nickname ? conn->nickname : "client", conn->s);
- conn->circ_id_type = CIRC_ID_TYPE_LOWER;
- } else if (!id_is_high && conn->circ_id_type == CIRC_ID_TYPE_LOWER) {
- log_info(LD_OR, "Got a low circuit ID from %s (%d); switching to "
- "high circuit IDs.",
- conn->nickname ? conn->nickname : "client", conn->s);
- conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
+ if (!server_mode(get_options())) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received create cell (type %d) from %s:%d, but we're a client. "
+ "Sending back a destroy.",
+ (int)cell->command, conn->_base.address, conn->_base.port);
+ connection_or_send_destroy(cell->circ_id, conn,
+ END_CIRC_REASON_TORPROTOCOL);
+ return;
}
- circ = circuit_get_by_circid_orconn(cell->circ_id, conn);
+ /* If the high bit of the circuit ID is not as expected, close the
+ * circ. */
+ id_is_high = cell->circ_id & (1<<15);
+ if ((id_is_high && conn->circ_id_type == CIRC_ID_TYPE_HIGHER) ||
+ (!id_is_high && conn->circ_id_type == CIRC_ID_TYPE_LOWER)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received create cell with unexpected circ_id %d. Closing.",
+ cell->circ_id);
+ connection_or_send_destroy(cell->circ_id, conn,
+ END_CIRC_REASON_TORPROTOCOL);
+ return;
+ }
- if (circ) {
+ if (circuit_get_by_circid_orconn(cell->circ_id, conn)) {
routerinfo_t *router = router_get_by_digest(conn->identity_digest);
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "received CREATE cell (circID %d) for known circ. "
+ "Received CREATE cell (circID %d) for known circ. "
"Dropping (age %d).",
- cell->circ_id, (int)(time(NULL) - conn->timestamp_created));
+ cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created));
if (router)
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Details: nickname \"%s\", platform %s.",
@@ -216,34 +218,34 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
return;
}
- circ = circuit_new(cell->circ_id, conn);
- circ->purpose = CIRCUIT_PURPOSE_OR;
- circuit_set_state(circ, CIRCUIT_STATE_ONIONSKIN_PENDING);
+ circ = or_circuit_new(cell->circ_id, conn);
+ circ->_base.purpose = CIRCUIT_PURPOSE_OR;
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_ONIONSKIN_PENDING);
if (cell->command == CELL_CREATE) {
- circ->onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN);
- memcpy(circ->onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN);
+ circ->_base.onionskin = tor_malloc(ONIONSKIN_CHALLENGE_LEN);
+ memcpy(circ->_base.onionskin, cell->payload, ONIONSKIN_CHALLENGE_LEN);
- /* hand it off to the cpuworkers, and then return */
+ /* hand it off to the cpuworkers, and then return. */
if (assign_to_cpuworker(NULL, CPUWORKER_TASK_ONION, circ) < 0) {
log_warn(LD_GENERAL,"Failed to hand off onionskin. Closing.");
- circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return;
}
log_debug(LD_OR,"success: handed off onionskin.");
} else {
/* This is a CREATE_FAST cell; we can handle it immediately without using
- * a CPU worker.*/
+ * a CPU worker. */
char keys[CPATH_KEY_MATERIAL_LEN];
char reply[DIGEST_LEN*2];
tor_assert(cell->command == CELL_CREATE_FAST);
if (fast_server_handshake(cell->payload, reply, keys, sizeof(keys))<0) {
log_warn(LD_OR,"Failed to generate key material. Closing.");
- circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return;
}
if (onionskin_answer(circ, CELL_CREATED_FAST, reply, keys)<0) {
log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing.");
- circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return;
}
}
@@ -258,7 +260,7 @@ command_process_create_cell(cell_t *cell, connection_t *conn)
* extend to the next hop in the circuit if necessary.
*/
static void
-command_process_created_cell(cell_t *cell, connection_t *conn)
+command_process_created_cell(cell_t *cell, or_connection_t *conn)
{
circuit_t *circ;
@@ -273,20 +275,22 @@ command_process_created_cell(cell_t *cell, connection_t *conn)
if (circ->n_circ_id != cell->circ_id) {
log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,
- "got created cell from OPward? Closing.");
+ "got created cell from Tor client? Closing.");
circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL);
return;
}
if (CIRCUIT_IS_ORIGIN(circ)) { /* we're the OP. Handshake this. */
+ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
log_debug(LD_OR,"at OP. Finishing handshake.");
- if (circuit_finish_handshake(circ, cell->command, cell->payload) < 0) {
+ if (circuit_finish_handshake(origin_circ, cell->command,
+ cell->payload) < 0) {
log_warn(LD_OR,"circuit_finish_handshake failed.");
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
return;
}
log_debug(LD_OR,"Moving to next skin.");
- if (circuit_send_next_onion_skin(circ) < 0) {
+ if (circuit_send_next_onion_skin(origin_circ) < 0) {
log_info(LD_OR,"circuit_send_next_onion_skin failed.");
/* XXX push this circuit_close lower */
circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
@@ -305,7 +309,7 @@ command_process_created_cell(cell_t *cell, connection_t *conn)
* circuit_receive_relay_cell() for actual processing.
*/
static void
-command_process_relay_cell(cell_t *cell, connection_t *conn)
+command_process_relay_cell(cell_t *cell, or_connection_t *conn)
{
circuit_t *circ;
int reason;
@@ -315,7 +319,7 @@ command_process_relay_cell(cell_t *cell, connection_t *conn)
if (!circ) {
log_debug(LD_OR,
"unknown circuit %d on connection from %s:%d. Dropping.",
- cell->circ_id, conn->address, conn->port);
+ cell->circ_id, conn->_base.address, conn->_base.port);
return;
}
@@ -325,7 +329,9 @@ command_process_relay_cell(cell_t *cell, connection_t *conn)
return;
}
- if (cell->circ_id == circ->p_circ_id) { /* it's an outgoing cell */
+ if (!CIRCUIT_IS_ORIGIN(circ) &&
+ cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) {
+ /* it's an outgoing cell */
if ((reason = circuit_receive_relay_cell(cell, circ,
CELL_DIRECTION_OUT)) < 0) {
log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,"circuit_receive_relay_cell "
@@ -358,7 +364,7 @@ command_process_relay_cell(cell_t *cell, connection_t *conn)
* and passes the destroy cell onward if necessary).
*/
static void
-command_process_destroy_cell(cell_t *cell, connection_t *conn)
+command_process_destroy_cell(cell_t *cell, or_connection_t *conn)
{
circuit_t *circ;
uint8_t reason;
@@ -367,17 +373,18 @@ command_process_destroy_cell(cell_t *cell, connection_t *conn)
reason = (uint8_t)cell->payload[0];
if (!circ) {
log_info(LD_OR,"unknown circuit %d on connection from %s:%d. Dropping.",
- cell->circ_id, conn->address, conn->port);
+ cell->circ_id, conn->_base.address, conn->_base.port);
return;
}
log_debug(LD_OR,"Received for circID %d.",cell->circ_id);
- if (cell->circ_id == circ->p_circ_id) {
+ if (!CIRCUIT_IS_ORIGIN(circ) &&
+ cell->circ_id == TO_OR_CIRCUIT(circ)->p_circ_id) {
/* the destroy came from behind */
- circuit_set_circid_orconn(circ, 0, NULL, P_CONN_CHANGED);
+ circuit_set_p_circid_orconn(TO_OR_CIRCUIT(circ), 0, NULL);
circuit_mark_for_close(circ, reason);
} else { /* the destroy came from ahead */
- circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
+ circuit_set_n_circid_orconn(circ, 0, NULL);
if (CIRCUIT_IS_ORIGIN(circ)) {
circuit_mark_for_close(circ, reason);
} else {
diff --git a/src/or/config.c b/src/or/config.c
index d8ae9a47c2..523ec22bfe 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -76,6 +76,8 @@ static config_abbrev_t _option_abbrevs[] = {
{ "NumHelperNodes", "NumEntryGuards", 0, 0},
{ "UseEntryNodes", "UseEntryGuards", 0, 0},
{ "NumEntryNodes", "NumEntryGuards", 0, 0},
+ { "ResolvConf", "ServerDNSResolvConfFile", 0, 1},
+ { "SearchDomains", "ServerDNSSearchDomains", 0, 1},
{ NULL, NULL, 0, 0},
};
/* A list of state-file abbreviations, for compatibility. */
@@ -100,10 +102,6 @@ typedef struct config_var_t {
const char *initvalue; /**< String (or null) describing initial value. */
} config_var_t;
-/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
-#define STRUCT_OFFSET(tp, member) \
- ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
-
#define STRUCT_VAR_P(st, off) \
((void*) ( ((char*)st) + off ) )
@@ -134,6 +132,7 @@ static config_var_t _option_vars[] = {
VAR("AuthDirReject", LINELIST, AuthDirReject, NULL),
VAR("AuthDirRejectUnlisted",BOOL, AuthDirRejectUnlisted,"0"),
VAR("AuthoritativeDirectory",BOOL, AuthoritativeDir, "0"),
+ VAR("AvoidDiskWrites", BOOL, AvoidDiskWrites, "0"),
VAR("BandwidthBurst", MEMUNIT, BandwidthBurst, "6 MB"),
VAR("BandwidthRate", MEMUNIT, BandwidthRate, "3 MB"),
VAR("CircuitBuildTimeout", INTERVAL, CircuitBuildTimeout, "1 minute"),
@@ -148,8 +147,7 @@ static config_var_t _option_vars[] = {
VAR("DebugLogFile", STRING, DebugLogFile, NULL),
VAR("DirAllowPrivateAddresses",BOOL, DirAllowPrivateAddresses, NULL),
VAR("DirListenAddress", LINELIST, DirListenAddress, NULL),
- /* if DirFetchPeriod is 0, see get_dir_fetch_period() in main.c */
- VAR("DirFetchPeriod", INTERVAL, DirFetchPeriod, "0 seconds"),
+ OBSOLETE("DirFetchPeriod"),
VAR("DirPolicy", LINELIST, DirPolicy, NULL),
VAR("DirPort", UINT, DirPort, "0"),
OBSOLETE("DirPostPeriod"),
@@ -184,7 +182,7 @@ static config_var_t _option_vars[] = {
VAR("LogFile", LINELIST_S, OldLogOptions, NULL),
VAR("LogLevel", LINELIST_S, OldLogOptions, NULL),
VAR("LongLivedPorts", CSV, LongLivedPorts,
- "21,22,706,1863,5050,5190,5222,5223,6667,8300,8888"),
+ "21,22,706,1863,5050,5190,5222,5223,6667,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
VAR("MaxAdvertisedBandwidth",MEMUNIT,MaxAdvertisedBandwidth,"128 TB"),
VAR("MaxCircuitDirtiness", INTERVAL, MaxCircuitDirtiness, "10 minutes"),
@@ -213,6 +211,8 @@ static config_var_t _option_vars[] = {
VAR("RecommendedClientVersions", LINELIST, RecommendedClientVersions, NULL),
VAR("RecommendedServerVersions", LINELIST, RecommendedServerVersions, NULL),
VAR("RedirectExit", LINELIST, RedirectExit, NULL),
+ VAR("RelayBandwidthBurst", MEMUNIT, RelayBandwidthBurst, "0"),
+ VAR("RelayBandwidthRate", MEMUNIT, RelayBandwidthRate, "0"),
VAR("RendExcludeNodes", STRING, RendExcludeNodes, NULL),
VAR("RendNodes", STRING, RendNodes, NULL),
VAR("RendPostPeriod", INTERVAL, RendPostPeriod, "1 hour"),
@@ -222,13 +222,15 @@ static config_var_t _option_vars[] = {
VAR("RunTesting", BOOL, RunTesting, "0"),
VAR("SafeLogging", BOOL, SafeLogging, "1"),
VAR("SafeSocks", BOOL, SafeSocks, "0"),
+ VAR("ServerDNSDetectHijacking",BOOL, ServerDNSDetectHijacking,"1"),
+ VAR("ServerDNSResolvConfFile", STRING, ServerDNSResolvConfFile, NULL),
+ VAR("ServerDNSSearchDomains", BOOL, ServerDNSSearchDomains, "0"),
VAR("ShutdownWaitLength", INTERVAL, ShutdownWaitLength, "30 seconds"),
VAR("SocksListenAddress", LINELIST, SocksListenAddress, NULL),
VAR("SocksPolicy", LINELIST, SocksPolicy, NULL),
VAR("SocksPort", UINT, SocksPort, "9050"),
VAR("SocksTimeout", INTERVAL, SocksTimeout, "2 minutes"),
- /* if StatusFetchPeriod is 0, see get_status_fetch_period() in main.c */
- VAR("StatusFetchPeriod", INTERVAL, StatusFetchPeriod, "0 seconds"),
+ OBSOLETE("StatusFetchPeriod"),
VAR("StrictEntryNodes", BOOL, StrictEntryNodes, "0"),
VAR("StrictExitNodes", BOOL, StrictExitNodes, "0"),
VAR("SysLog", LINELIST_S, OldLogOptions, NULL),
@@ -237,6 +239,8 @@ static config_var_t _option_vars[] = {
VAR("TrackHostExits", CSV, TrackHostExits, NULL),
VAR("TrackHostExitsExpire",INTERVAL, TrackHostExitsExpire, "30 minutes"),
OBSOLETE("TrafficShaping"),
+ VAR("TransListenAddress", LINELIST, TransListenAddress, NULL),
+ VAR("TransPort", UINT, TransPort, "0"),
VAR("UseEntryGuards", BOOL, UseEntryGuards, "1"),
VAR("User", STRING, User, NULL),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
@@ -391,11 +395,15 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options,
static uint64_t config_parse_memunit(const char *s, int *ok);
static int config_parse_interval(const char *s, int *ok);
-static void print_cvs_version(void);
+static void print_svn_version(void);
static void init_libevent(void);
static int opt_streq(const char *s1, const char *s2);
+typedef enum {
+ LE_OLD=0, LE_10C, LE_10D, LE_10E, LE_11, LE_11A, LE_11B, LE_OTHER
+} le_version_t;
+static le_version_t decode_libevent_version(void);
#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
-static void check_libevent_version(const char *m, const char *v, int server);
+static void check_libevent_version(const char *m, int server);
#endif
/*static*/ or_options_t *options_new(void);
@@ -445,7 +453,7 @@ static or_state_t *global_state = NULL;
static void *
config_alloc(config_format_t *fmt)
{
- void *opts = opts = tor_malloc_zero(fmt->size);
+ void *opts = tor_malloc_zero(fmt->size);
*(uint32_t*)STRUCT_VAR_P(opts, fmt->magic_offset) = fmt->magic;
CHECK(fmt, opts);
return opts;
@@ -514,7 +522,9 @@ safe_str(const char *address)
return address;
}
-/** Equivalent to escaped(safe_str(address)) */
+/** Equivalent to escaped(safe_str(address)). See reentrancy node on
+ * escaped(): don't use this outside the main thread, or twice in the same
+ * log statement. */
const char *
escaped_safe_str(const char *address)
{
@@ -530,6 +540,7 @@ add_default_trusted_dirservers(void)
{
int i;
const char *dirservers[] = {
+ /* eventually we should mark moria1 as "v1only" */
"moria1 v1 18.244.0.188:9031 "
"FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441",
"moria2 v1 18.244.0.114:80 "
@@ -787,18 +798,22 @@ options_act(or_options_t *old_options)
log_info(LD_GENERAL,
"Worker-related options changed. Rotating workers.");
if (server_mode(options) && !server_mode(old_options)) {
- extern int has_completed_circuit;
if (init_keys() < 0) {
log_err(LD_GENERAL,"Error initializing keys; exiting");
return -1;
}
server_has_changed_ip();
- if (has_completed_circuit)
+ if (has_completed_circuit || !any_predicted_circuits(time(NULL)))
inform_testing_reachability();
}
cpuworkers_rotate();
- dnsworkers_rotate();
+ dns_reset();
+ }
+#ifdef USE_EVENTDNS
+ else {
+ dns_reset();
}
+#endif
}
/* Check if we need to parse and add the EntryNodes config option. */
@@ -807,6 +822,12 @@ options_act(or_options_t *old_options)
!opt_streq(old_options->EntryNodes, options->EntryNodes)))
entry_nodes_should_be_added();
+ /* If the user wants to avoid certain nodes, make sure none of them
+ * are already entryguards */
+ if (options->ExcludeNodes) {
+ // XXX TODO
+ }
+
/* Since our options changed, we might need to regenerate and upload our
* server descriptor.
*/
@@ -1274,19 +1295,9 @@ get_assigned_option(config_format_t *fmt, or_options_t *options,
if (!var) {
log_warn(LD_CONFIG, "Unknown option '%s'. Failing.", key);
return NULL;
- } else if (var->type == CONFIG_TYPE_LINELIST_S) {
- log_warn(LD_CONFIG,
- "Can't return context-sensitive '%s' on its own", key);
- return NULL;
}
value = STRUCT_VAR_P(options, var->var_offset);
- if (var->type == CONFIG_TYPE_LINELIST ||
- var->type == CONFIG_TYPE_LINELIST_V) {
- /* Linelist requires special handling: we just copy and return it. */
- return config_lines_dup(*(const config_line_t**)value);
- }
-
result = tor_malloc_zero(sizeof(config_line_t));
result->key = tor_strdup(var->name);
switch (var->type)
@@ -1342,6 +1353,17 @@ get_assigned_option(config_format_t *fmt, or_options_t *options,
tor_free(result->key);
tor_free(result);
return NULL;
+ case CONFIG_TYPE_LINELIST_S:
+ log_warn(LD_CONFIG,
+ "Can't return context-sensitive '%s' on its own", key);
+ tor_free(result->key);
+ tor_free(result);
+ return NULL;
+ case CONFIG_TYPE_LINELIST:
+ case CONFIG_TYPE_LINELIST_V:
+ tor_free(result->key);
+ tor_free(result);
+ return config_lines_dup(*(const config_line_t**)value);
default:
tor_free(result->key);
tor_free(result);
@@ -1491,6 +1513,7 @@ static void
option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var)
{
void *lvalue = STRUCT_VAR_P(options, var->var_offset);
+ (void)fmt; /* unused */
switch (var->type) {
case CONFIG_TYPE_STRING:
tor_free(*(char**)lvalue);
@@ -1573,8 +1596,8 @@ print_usage(void)
* public IP address.
*/
int
-resolve_my_address(or_options_t *options, uint32_t *addr_out,
- char **hostname_out)
+resolve_my_address(int warn_severity, or_options_t *options,
+ uint32_t *addr_out, char **hostname_out)
{
struct in_addr in;
struct hostent *rent;
@@ -1584,6 +1607,8 @@ resolve_my_address(or_options_t *options, uint32_t *addr_out,
char tmpbuf[INET_NTOA_BUF_LEN];
static uint32_t old_addr=0;
const char *address = options->Address;
+ int notice_severity = warn_severity <= LOG_NOTICE ?
+ LOG_NOTICE : warn_severity;
tor_assert(addr_out);
@@ -1594,13 +1619,13 @@ resolve_my_address(or_options_t *options, uint32_t *addr_out,
explicit_hostname = 0; /* it's implicit */
if (gethostname(hostname, sizeof(hostname)) < 0) {
- log_warn(LD_NET,"Error obtaining local hostname");
+ log_fn(warn_severity, LD_NET,"Error obtaining local hostname");
return -1;
}
log_debug(LD_CONFIG,"Guessed local host name as '%s'",hostname);
}
- /* now we know hostname. resolve it and keep only the IP */
+ /* now we know hostname. resolve it and keep only the IP address */
if (tor_inet_aton(hostname, &in) == 0) {
/* then we have to resolve it */
@@ -1610,21 +1635,22 @@ resolve_my_address(or_options_t *options, uint32_t *addr_out,
uint32_t interface_ip;
if (explicit_hostname) {
- log_warn(LD_CONFIG,"Could not resolve local Address '%s'. Failing.",
- hostname);
+ log_fn(warn_severity, LD_CONFIG,
+ "Could not resolve local Address '%s'. Failing.", hostname);
return -1;
}
- log_notice(LD_CONFIG, "Could not resolve guessed local hostname '%s'. "
- "Trying something else.", hostname);
- if (get_interface_address(&interface_ip)) {
- log_warn(LD_CONFIG, "Could not get local interface IP address. "
- "Failing.");
+ log_fn(notice_severity, LD_CONFIG,
+ "Could not resolve guessed local hostname '%s'. "
+ "Trying something else.", hostname);
+ if (get_interface_address(warn_severity, &interface_ip)) {
+ log_fn(warn_severity, LD_CONFIG,
+ "Could not get local interface IP address. Failing.");
return -1;
}
in.s_addr = htonl(interface_ip);
tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
- log_notice(LD_CONFIG, "Learned IP address '%s' for local interface."
- " Using that.", tmpbuf);
+ log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for "
+ "local interface. Using that.", tmpbuf);
strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
} else {
tor_assert(rent->h_length == 4);
@@ -1635,24 +1661,26 @@ resolve_my_address(or_options_t *options, uint32_t *addr_out,
uint32_t interface_ip;
tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
- log_notice(LD_CONFIG, "Guessed local hostname '%s' resolves to a "
- "private IP address (%s). Trying something else.", hostname,
- tmpbuf);
+ log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' "
+ "resolves to a private IP address (%s). Trying something "
+ "else.", hostname, tmpbuf);
- if (get_interface_address(&interface_ip)) {
- log_warn(LD_CONFIG, "Could not get local interface IP address. "
- "Too bad.");
+ if (get_interface_address(warn_severity, &interface_ip)) {
+ log_fn(warn_severity, LD_CONFIG,
+ "Could not get local interface IP address. Too bad.");
} else if (is_internal_IP(interface_ip, 0)) {
struct in_addr in2;
in2.s_addr = htonl(interface_ip);
tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf));
- log_notice(LD_CONFIG, "Interface IP '%s' is a private address "
- "too. Ignoring.", tmpbuf);
+ log_fn(notice_severity, LD_CONFIG,
+ "Interface IP address '%s' is a private address too. "
+ "Ignoring.", tmpbuf);
} else {
in.s_addr = htonl(interface_ip);
tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
- log_notice(LD_CONFIG, "Learned IP address '%s' for local interface."
- " Using that.", tmpbuf);
+ log_fn(notice_severity, LD_CONFIG,
+ "Learned IP address '%s' for local interface."
+ " Using that.", tmpbuf);
strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
}
}
@@ -1666,18 +1694,18 @@ resolve_my_address(or_options_t *options, uint32_t *addr_out,
if (!options->DirServers) {
/* if they are using the default dirservers, disallow internal IPs
* always. */
- log_warn(LD_CONFIG,"Address '%s' resolves to private IP '%s'. "
- "Tor servers that use the default DirServers must have public "
- "IP addresses.",
- hostname, tmpbuf);
+ log_fn(warn_severity, LD_CONFIG,
+ "Address '%s' resolves to private IP address '%s'. "
+ "Tor servers that use the default DirServers must have public "
+ "IP addresses.", hostname, tmpbuf);
return -1;
}
if (!explicit_ip) {
/* even if they've set their own dirservers, require an explicit IP if
* they're using an internal address. */
- log_warn(LD_CONFIG,"Address '%s' resolves to private IP '%s'. Please "
- "set the Address config option to be the IP you want to use.",
- hostname, tmpbuf);
+ log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private "
+ "IP address '%s'. Please set the Address config option to be "
+ "the IP address you want to use.", hostname, tmpbuf);
return -1;
}
}
@@ -1685,7 +1713,9 @@ resolve_my_address(or_options_t *options, uint32_t *addr_out,
log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf);
*addr_out = ntohl(in.s_addr);
if (old_addr && old_addr != *addr_out) {
- log_notice(LD_NET, "Your IP seems to have changed. Updating.");
+ /* Leave this as a notice, regardless of the requested severity,
+ * at least until dynamic IP address support becomes bulletproof. */
+ log_notice(LD_NET, "Your IP address seems to have changed. Updating.");
server_has_changed_ip();
}
old_addr = *addr_out;
@@ -1963,23 +1993,40 @@ validate_ports_csv(smartlist_t *sl, const char *name, char **msg)
return 0;
}
-/** Lowest allowable value for DirFetchPeriod; if this is too low, clients can
- * overload the directory system. */
-#define MIN_DIR_FETCH_PERIOD (10*60)
+#if 0
+/* XXXX Unused. */
+/** Return 0 if every element of sl is a string holding an IP address, or if sl
+ * is NULL. Otherwise set *msg and return -1. */
+static int
+validate_ips_csv(smartlist_t *sl, const char *name, char **msg)
+{
+ char buf[1024];
+ tor_assert(name);
+
+ if (!sl)
+ return 0;
+
+ SMARTLIST_FOREACH(sl, const char *, cp,
+ {
+ struct in_addr in;
+ if (0 == tor_inet_aton(cp, &in)) {
+ int r = tor_snprintf(buf, sizeof(buf),
+ "Malformed address '%s' out of range in %s", cp, name);
+ *msg = tor_strdup(r >= 0 ? buf : "internal error");
+ return -1;
+ }
+ });
+ return 0;
+}
+#endif
+
/** Lowest allowable value for RendPostPeriod; if this is too low, hidden
* services can overload the directory system. */
#define MIN_REND_POST_PERIOD (5*60)
-/** Lowest allowable value for StatusFetchPeriod; if this is too low, clients
- * can overload the directory system. */
-#define MIN_STATUS_FETCH_PERIOD (5*60)
/** Highest allowable value for DirFetchPeriod, StatusFetchPeriod, and
* RendPostPeriod. */
#define MAX_DIR_PERIOD (MIN_ONION_KEY_LIFETIME/2)
-/** Highest allowable value for DirFetchPeriod for directory caches. */
-#define MAX_CACHE_DIR_FETCH_PERIOD (60*60)
-/** Highest allowable value for StatusFetchPeriod for directory caches. */
-#define MAX_CACHE_STATUS_FETCH_PERIOD (15*60)
/** Return 0 if every setting in <b>options</b> is reasonable, and a
* permissible transition from <b>old_options</b>. Else return -1.
@@ -2031,27 +2078,39 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->ControlPort == 0 && options->ControlListenAddress != NULL)
REJECT("ControlPort must be defined if ControlListenAddress is defined.");
+ if (options->TransPort == 0 && options->TransListenAddress != NULL)
+ REJECT("TransPort must be defined if TransListenAddress is defined.");
+
#if 0 /* don't complain, since a standard configuration does this! */
if (options->SocksPort == 0 && options->SocksListenAddress != NULL)
REJECT("SocksPort must be defined if SocksListenAddress is defined.");
#endif
- if (options->SocksListenAddress) {
- config_line_t *line = NULL;
- char *address = NULL;
- for (line = options->SocksListenAddress; line; line = line->next) {
+ for (i = 0; i < 2; ++i) {
+ int is_socks = i==0;
+ config_line_t *line, *opt, *old;
+ const char *tp = is_socks ? "SOCKS proxy" : "transparent proxy";
+ if (is_socks) {
+ opt = options->SocksListenAddress;
+ old = old_options ? old_options->SocksListenAddress : NULL;
+ } else {
+ opt = options->TransListenAddress;
+ old = old_options ? old_options->TransListenAddress : NULL;
+ }
+
+ for (line = opt; line; line = line->next) {
+ char *address = NULL;
uint16_t port;
uint32_t addr;
- if (parse_addr_port(line->value, &address, &addr, &port)<0)
+ if (parse_addr_port(LOG_WARN, line->value, &address, &addr, &port)<0)
continue; /* We'll warn about this later. */
if (!is_internal_IP(addr, 1) &&
- (!old_options || !config_lines_eq(old_options->SocksListenAddress,
- options->SocksListenAddress))) {
+ (!old_options || !config_lines_eq(old, opt))) {
log_warn(LD_CONFIG,
- "You specified a public address '%s' for a SOCKS listener. Other "
+ "You specified a public address '%s' for a %s. Other "
"people on the Internet might find your computer and use it as "
- "an open SOCKS proxy. Please don't allow this unless you have "
- "a good reason.", address);
+ "an open %s. Please don't allow this unless you have "
+ "a good reason.", address, tp, tp);
}
tor_free(address);
}
@@ -2098,10 +2157,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->PublishServerDescriptor = 0;
}
- if (server_mode(options)) {
+ if (authdir_mode(options)) {
/* confirm that our address isn't broken, so we can complain now */
uint32_t tmp;
- if (resolve_my_address(options, &tmp, NULL) < 0)
+ if (resolve_my_address(LOG_WARN, options, &tmp, NULL) < 0)
REJECT("Failed to resolve/guess local address. See logs for details.");
}
@@ -2113,8 +2172,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->SocksPort < 0 || options->SocksPort > 65535)
REJECT("SocksPort option out of bounds.");
- if (options->SocksPort == 0 && options->ORPort == 0)
- REJECT("SocksPort and ORPort are both undefined? Quitting.");
+ if (options->TransPort < 0 || options->TransPort > 65535)
+ REJECT("TransPort option out of bounds.");
+
+ if (options->SocksPort == 0 && options->TransPort == 0 &&
+ options->ORPort == 0)
+ REJECT("SocksPort, TransPort, and ORPort are all undefined? Quitting.");
if (options->ControlPort < 0 || options->ControlPort > 65535)
REJECT("ControlPort option out of bounds.");
@@ -2122,6 +2185,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->DirPort < 0 || options->DirPort > 65535)
REJECT("DirPort option out of bounds.");
+#ifndef USE_TRANSPARENT
+ if (options->TransPort || options->TransListenAddress)
+ REJECT("TransPort and TransListenAddress are disabled in this build.");
+#endif
+
if (options->StrictExitNodes &&
(!options->ExitNodes || !strlen(options->ExitNodes)) &&
(!old_options ||
@@ -2296,51 +2364,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
(options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0))
REJECT("PathlenCoinWeight option must be >=0.0 and <1.0.");
- if (options->DirFetchPeriod &&
- options->DirFetchPeriod < MIN_DIR_FETCH_PERIOD) {
- log(LOG_WARN, LD_CONFIG,
- "DirFetchPeriod option must be at least %d seconds. Clipping.",
- MIN_DIR_FETCH_PERIOD);
- options->DirFetchPeriod = MIN_DIR_FETCH_PERIOD;
- }
- if (options->StatusFetchPeriod &&
- options->StatusFetchPeriod < MIN_STATUS_FETCH_PERIOD) {
- log(LOG_WARN, LD_CONFIG,
- "StatusFetchPeriod option must be at least %d seconds. Clipping.",
- MIN_STATUS_FETCH_PERIOD);
- options->StatusFetchPeriod = MIN_STATUS_FETCH_PERIOD;
- }
if (options->RendPostPeriod < MIN_REND_POST_PERIOD) {
log(LOG_WARN,LD_CONFIG,"RendPostPeriod option must be at least %d seconds."
" Clipping.", MIN_REND_POST_PERIOD);
options->RendPostPeriod = MIN_REND_POST_PERIOD;
}
- if (options->DirPort && ! options->AuthoritativeDir) {
- if (options->DirFetchPeriod > MAX_CACHE_DIR_FETCH_PERIOD) {
- log(LOG_WARN, LD_CONFIG, "Caching directory servers must have "
- "DirFetchPeriod less than %d seconds. Clipping.",
- MAX_CACHE_DIR_FETCH_PERIOD);
- options->DirFetchPeriod = MAX_CACHE_DIR_FETCH_PERIOD;
- }
- if (options->StatusFetchPeriod > MAX_CACHE_STATUS_FETCH_PERIOD) {
- log(LOG_WARN, LD_CONFIG, "Caching directory servers must have "
- "StatusFetchPeriod less than %d seconds. Clipping.",
- MAX_CACHE_STATUS_FETCH_PERIOD);
- options->StatusFetchPeriod = MAX_CACHE_STATUS_FETCH_PERIOD;
- }
- }
-
- if (options->DirFetchPeriod > MAX_DIR_PERIOD) {
- log(LOG_WARN, LD_CONFIG, "DirFetchPeriod is too large; clipping to %ds.",
- MAX_DIR_PERIOD);
- options->DirFetchPeriod = MAX_DIR_PERIOD;
- }
- if (options->StatusFetchPeriod > MAX_DIR_PERIOD) {
- log(LOG_WARN, LD_CONFIG,"StatusFetchPeriod is too large; clipping to %ds.",
- MAX_DIR_PERIOD);
- options->StatusFetchPeriod = MAX_DIR_PERIOD;
- }
if (options->RendPostPeriod > MAX_DIR_PERIOD) {
log(LOG_WARN, LD_CONFIG, "RendPostPeriod is too large; clipping to %ds.",
MAX_DIR_PERIOD);
@@ -2379,7 +2408,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to parse accounting options. See logs for details.");
if (options->HttpProxy) { /* parse it now */
- if (parse_addr_port(options->HttpProxy, NULL,
+ if (parse_addr_port(LOG_WARN, options->HttpProxy, NULL,
&options->HttpProxyAddr, &options->HttpProxyPort) < 0)
REJECT("HttpProxy failed to parse or resolve. Please fix.");
if (options->HttpProxyPort == 0) { /* give it a default */
@@ -2393,7 +2422,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->HttpsProxy) { /* parse it now */
- if (parse_addr_port(options->HttpsProxy, NULL,
+ if (parse_addr_port(LOG_WARN, options->HttpsProxy, NULL,
&options->HttpsProxyAddr, &options->HttpsProxyPort) <0)
REJECT("HttpsProxy failed to parse or resolve. Please fix.");
if (options->HttpsProxyPort == 0) { /* give it a default */
@@ -2416,6 +2445,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->UseEntryGuards && ! options->NumEntryGuards)
REJECT("Cannot enable UseEntryGuards with NumEntryGuards set to 0");
+#ifndef USE_EVENTDNS
+ if (options->ServerDNSResolvConfFile)
+ log(LOG_WARN, LD_CONFIG,
+ "ServerDNSResolvConfFile only works when eventdns support is enabled.");
+#endif
+
if (check_nickname_list(options->ExitNodes, "ExitNodes", msg))
return -1;
if (check_nickname_list(options->EntryNodes, "EntryNodes", msg))
@@ -2539,6 +2574,8 @@ options_transition_affects_workers(or_options_t *old_options,
if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
old_options->NumCpus != new_options->NumCpus ||
old_options->ORPort != new_options->ORPort ||
+ old_options->ServerDNSSearchDomains !=
+ new_options->ServerDNSSearchDomains ||
old_options->SafeLogging != new_options->SafeLogging ||
!config_lines_eq(old_options->Logs, new_options->Logs))
return 1;
@@ -2697,7 +2734,7 @@ options_init_from_torrc(int argc, char **argv)
if (argc > 1 && (!strcmp(argv[1],"--version"))) {
printf("Tor version %s.\n",VERSION);
if (argc > 2 && (!strcmp(argv[2],"--version"))) {
- print_cvs_version();
+ print_svn_version();
}
exit(0);
}
@@ -2732,11 +2769,11 @@ options_init_from_torrc(int argc, char **argv)
if (using_default_torrc) {
/* didn't find one, try CONFDIR */
const char *dflt = get_default_conf_file();
- char *fn = NULL;
if (dflt && file_status(dflt) == FN_FILE) {
fname = tor_strdup(dflt);
} else {
#ifndef MS_WINDOWS
+ char *fn;
fn = expand_filename("~/.torrc");
if (fn && file_status(fn) == FN_FILE) {
fname = fn;
@@ -3168,8 +3205,8 @@ parse_redirect_line(smartlist_t *result, config_line_t *line, char **msg)
if (0==strcasecmp(smartlist_get(elements,1), "pass")) {
r->is_redirect = 0;
} else {
- if (parse_addr_port(smartlist_get(elements,1),NULL,&r->addr_dest,
- &r->port_dest)) {
+ if (parse_addr_port(LOG_WARN, smartlist_get(elements,1),NULL,
+ &r->addr_dest, &r->port_dest)) {
*msg = tor_strdup("Error parsing dest address in RedirectExit line");
goto err;
}
@@ -3234,7 +3271,7 @@ parse_dir_server_line(const char *line, int validate_only)
goto err;
}
addrport = smartlist_get(items, 0);
- if (parse_addr_port(addrport, &address, NULL, &port)<0) {
+ if (parse_addr_port(LOG_WARN, addrport, &address, NULL, &port)<0) {
log_warn(LD_CONFIG, "Error parsing DirServer address '%s'", addrport);
goto err;
}
@@ -3364,6 +3401,8 @@ write_configuration_file(const char *fname, or_options_t *options)
break;
case FN_NOENT:
break;
+ case FN_ERROR:
+ case FN_DIR:
default:
log_warn(LD_CONFIG,
"Config file \"%s\" is not a file? Failing.", fname);
@@ -3555,7 +3594,20 @@ init_libevent(void)
*/
suppress_libevent_log_msg("Function not implemented");
#ifdef __APPLE__
- setenv("EVENT_NOKQUEUE","1",1);
+ if (decode_libevent_version() < LE_11B) {
+ setenv("EVENT_NOKQUEUE","1",1);
+ } else if (!getenv("EVENT_NOKQUEUE")) {
+ const char *ver = NULL;
+#ifdef HAVE_EVENT_GET_VERSION
+ ver = event_get_version();
+#endif
+ /* If we're 1.1b or later, we'd better have get_version() */
+ tor_assert(ver);
+ log(LOG_NOTICE, LD_GENERAL, "Enabling experimental OS X kqueue support "
+ "with libevent %s. If this turns out to not work, "
+ "set the environment variable EVENT_NOKQUEUE, and tell the Tor "
+ "developers.", ver);
+ }
#endif
event_init();
suppress_libevent_log_msg(NULL);
@@ -3565,46 +3617,78 @@ init_libevent(void)
log(LOG_NOTICE, LD_GENERAL,
"Initialized libevent version %s using method %s. Good.",
event_get_version(), event_get_method());
- check_libevent_version(event_get_method(), event_get_version(),
- get_options()->ORPort != 0);
+ check_libevent_version(event_get_method(), get_options()->ORPort != 0);
#else
log(LOG_NOTICE, LD_GENERAL,
"Initialized old libevent (version 1.0b or earlier).");
log(LOG_WARN, LD_GENERAL,
- "You have a very old version of libevent. It is likely to be buggy; "
- "please consider building Tor with a more recent version.");
+ "You have a *VERY* old version of libevent. It is likely to be buggy; "
+ "please build Tor with a more recent version.");
#endif
}
#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
+static const struct {
+ const char *name; le_version_t version;
+} le_version_table[] = {
+ /* earlier versions don't have get_version. */
+ { "1.0c", LE_10C },
+ { "1.0d", LE_10D },
+ { "1.0e", LE_10E },
+ { "1.1", LE_11 },
+ { "1.1a", LE_11A },
+ { "1.1b", LE_11B },
+ { NULL, 0 }
+};
+
+static le_version_t
+decode_libevent_version(void)
+{
+ const char *v = event_get_version();
+ int i;
+ for (i=0; le_version_table[i].name; ++i) {
+ if (!strcmp(le_version_table[i].name, v)) {
+ return le_version_table[i].version;
+ }
+ }
+ return LE_OTHER;
+}
+
/**
* Compare the given libevent method and version to a list of versions
* which are known not to work. Warn the user as appropriate.
*
*/
static void
-check_libevent_version(const char *m, const char *v, int server)
+check_libevent_version(const char *m, int server)
{
int buggy = 0, iffy = 0, slow = 0;
+ le_version_t version;
+ const char *v = event_get_version();
- tor_assert(m && v);
+ version = decode_libevent_version();
+ /* XXX Would it be worthwhile disabling the methods that we know
+ * are buggy, rather than just warning about them and then proceeding
+ * to use them? If so, we should probably not wrap this whole thing
+ * in HAVE_EVENT_GET_VERSION and HAVE_EVENT_GET_METHOD. -RD */
if (!strcmp(m, "kqueue")) {
- if (!strcmp(v, "1.0c") || !strcmp(v, "1.0d") || !strcmp(v, "1.0e") ||
- !strcmp(v, "1.1")) {
+ if (version < LE_11B)
buggy = 1;
- }
} else if (!strcmp(m, "epoll")) {
- if (!strcmp(v, "1.0c") || !strcmp(v, "1.0d") || !strcmp(v, "1.0e"))
+ if (version < LE_11)
iffy = 1;
} else if (!strcmp(m, "poll")) {
- if (!strcmp(v, "1.0c") || !strcmp(v, "1.0d"))
+ if (version < LE_10E)
buggy = 1;
- else if (!strcmp(v, "1.0e"))
+ else if (version < LE_11)
slow = 1;
} else if (!strcmp(m, "poll")) {
- if (!strcmp(v, "1.0c") || !strcmp(v, "1.0d") || !strcmp(v, "1.0e"))
+ if (version < LE_11)
slow = 1;
+ } else if (!strcmp(m, "win32")) {
+ if (version < LE_11B)
+ buggy = 1;
}
if (buggy) {
@@ -3623,6 +3707,12 @@ check_libevent_version(const char *m, const char *v, int server)
}
}
+#else
+static le_version_t
+decode_libevent_version(void)
+{
+ return LE_OLD;
+}
#endif
/** Return the persistent state struct for this Tor. */
@@ -3654,6 +3744,10 @@ static int
or_state_validate(or_state_t *old_state, or_state_t *state,
int from_setconf, char **msg)
{
+ /* We don't use these; only options do. Still, we need to match that
+ * signature. */
+ (void) from_setconf;
+ (void) old_state;
if (entry_guards_parse_state(state, 0, msg)<0) {
return -1;
}
@@ -3704,7 +3798,7 @@ or_state_load(void)
or_state_t *new_state = NULL;
char *contents = NULL, *fname;
char *errmsg = NULL;
- int r = -1;
+ int r = -1, badstate = 0;
fname = get_or_state_fname();
switch (file_status(fname)) {
@@ -3716,6 +3810,8 @@ or_state_load(void)
break;
case FN_NOENT:
break;
+ case FN_ERROR:
+ case FN_DIR:
default:
log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname);
goto done;
@@ -3732,30 +3828,68 @@ or_state_load(void)
lines, 0, 0, &errmsg);
config_free_lines(lines);
if (assign_retval<0)
- goto done;
+ badstate = 1;
+ if (errmsg) {
+ log_warn(LD_GENERAL, "%s", errmsg);
+ tor_free(errmsg);
+ }
}
- if (or_state_validate(NULL, new_state, 1, &errmsg) < 0) {
- goto done;
+ if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0)
+ badstate = 1;
+
+ if (errmsg) {
+ log_warn(LD_GENERAL, "%s", errmsg);
+ tor_free(errmsg);
}
- if (contents)
+ if (badstate && !contents) {
+ log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state."
+ " This is a bug in Tor.");
+ goto done;
+ } else if (badstate && contents) {
+ int i;
+ file_status_t status;
+ size_t len = strlen(fname)+16;
+ char *fname2 = tor_malloc(len);
+ for (i = 0; i < 100; ++i) {
+ tor_snprintf(fname2, len, "%s.%d", fname, i);
+ status = file_status(fname2);
+ if (status == FN_NOENT)
+ break;
+ }
+ if (i == 100) {
+ log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad "
+ "state files to move aside. Discarding the old state file.",
+ fname);
+ unlink(fname);
+ } else {
+ log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside "
+ "to \"%s\". This could be a bug in Tor; please tell "
+ "the developers.", fname, fname2);
+ rename(fname, fname2);
+ }
+ tor_free(fname2);
+ tor_free(contents);
+ config_free(&state_format, new_state);
+
+ new_state = tor_malloc_zero(sizeof(or_state_t));
+ new_state->_magic = OR_STATE_MAGIC;
+ config_init(&state_format, new_state);
+ } else if (contents) {
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
- else
+ } else {
log_info(LD_GENERAL, "Initialized state");
+ }
or_state_set(new_state);
new_state = NULL;
if (!contents) {
global_state->dirty = 1;
or_state_save();
}
-
r = 0;
+
done:
- if (errmsg) {
- log_warn(LD_GENERAL, "%s", errmsg);
- tor_free(errmsg);
- }
tor_free(fname);
tor_free(contents);
if (new_state)
@@ -3862,47 +3996,47 @@ config_getinfo_helper(const char *question, char **answer)
#include "../common/ht.h"
#include "../common/test.h"
-/** Dump the version of every file to the log. */
-static void
-print_cvs_version(void)
-{
- extern const char aes_c_id[];
- extern const char compat_c_id[];
- extern const char container_c_id[];
- extern const char crypto_c_id[];
- extern const char log_c_id[];
- extern const char torgzip_c_id[];
- extern const char tortls_c_id[];
- extern const char util_c_id[];
-
- extern const char buffers_c_id[];
- extern const char circuitbuild_c_id[];
- extern const char circuitlist_c_id[];
- extern const char circuituse_c_id[];
- extern const char command_c_id[];
+extern const char aes_c_id[];
+extern const char compat_c_id[];
+extern const char container_c_id[];
+extern const char crypto_c_id[];
+extern const char log_c_id[];
+extern const char torgzip_c_id[];
+extern const char tortls_c_id[];
+extern const char util_c_id[];
+
+extern const char buffers_c_id[];
+extern const char circuitbuild_c_id[];
+extern const char circuitlist_c_id[];
+extern const char circuituse_c_id[];
+extern const char command_c_id[];
// extern const char config_c_id[];
- extern const char connection_c_id[];
- extern const char connection_edge_c_id[];
- extern const char connection_or_c_id[];
- extern const char control_c_id[];
- extern const char cpuworker_c_id[];
- extern const char directory_c_id[];
- extern const char dirserv_c_id[];
- extern const char dns_c_id[];
- extern const char hibernate_c_id[];
- extern const char main_c_id[];
- extern const char onion_c_id[];
- extern const char policies_c_id[];
- extern const char relay_c_id[];
- extern const char rendclient_c_id[];
- extern const char rendcommon_c_id[];
- extern const char rendmid_c_id[];
- extern const char rendservice_c_id[];
- extern const char rephist_c_id[];
- extern const char router_c_id[];
- extern const char routerlist_c_id[];
- extern const char routerparse_c_id[];
+extern const char connection_c_id[];
+extern const char connection_edge_c_id[];
+extern const char connection_or_c_id[];
+extern const char control_c_id[];
+extern const char cpuworker_c_id[];
+extern const char directory_c_id[];
+extern const char dirserv_c_id[];
+extern const char dns_c_id[];
+extern const char hibernate_c_id[];
+extern const char main_c_id[];
+extern const char onion_c_id[];
+extern const char policies_c_id[];
+extern const char relay_c_id[];
+extern const char rendclient_c_id[];
+extern const char rendcommon_c_id[];
+extern const char rendmid_c_id[];
+extern const char rendservice_c_id[];
+extern const char rephist_c_id[];
+extern const char router_c_id[];
+extern const char routerlist_c_id[];
+extern const char routerparse_c_id[];
+/** Dump the version of every file to the log. */
+static void
+print_svn_version(void)
+{
puts(AES_H_ID);
puts(COMPAT_H_ID);
puts(CONTAINER_H_ID);
diff --git a/src/or/connection.c b/src/or/connection.c
index 3135c61584..88346f8aad 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -16,10 +16,12 @@ const char connection_c_id[] =
static connection_t *connection_create_listener(const char *listenaddress,
uint16_t listenport, int type);
-static int connection_init_accepted_conn(connection_t *conn);
+static int connection_init_accepted_conn(connection_t *conn,
+ uint8_t listener_type);
static int connection_handle_listener_read(connection_t *conn, int new_type);
-static int connection_receiver_bucket_should_increase(connection_t *conn);
+static int connection_receiver_bucket_should_increase(or_connection_t *conn);
static int connection_finished_flushing(connection_t *conn);
+static int connection_flushed_some(connection_t *conn);
static int connection_finished_connecting(connection_t *conn);
static int connection_reached_eof(connection_t *conn);
static int connection_read_to_buf(connection_t *conn, int *max_to_read);
@@ -43,6 +45,7 @@ conn_type_to_string(int type)
case CONN_TYPE_OR: return "OR";
case CONN_TYPE_EXIT: return "Exit";
case CONN_TYPE_AP_LISTENER: return "Socks listener";
+ case CONN_TYPE_AP_TRANS_LISTENER: return "Transparent listener";
case CONN_TYPE_AP: return "Socks";
case CONN_TYPE_DIR_LISTENER: return "Directory listener";
case CONN_TYPE_DIR: return "Directory";
@@ -68,6 +71,7 @@ conn_state_to_string(int type, int state)
switch (type) {
case CONN_TYPE_OR_LISTENER:
case CONN_TYPE_AP_LISTENER:
+ case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_DIR_LISTENER:
case CONN_TYPE_CONTROL_LISTENER:
if (state == LISTENER_STATE_READY)
@@ -92,6 +96,7 @@ conn_state_to_string(int type, int state)
break;
case CONN_TYPE_AP:
switch (state) {
+ case AP_CONN_STATE_ORIGDST_WAIT:
case AP_CONN_STATE_SOCKS_WAIT: return "waiting for dest info";
case AP_CONN_STATE_RENDDESC_WAIT: return "waiting for rendezvous desc";
case AP_CONN_STATE_CONTROLLER_WAIT: return "waiting for controller";
@@ -144,7 +149,7 @@ conn_state_to_string(int type, int state)
/** Allocate space for a new connection_t. This function just initializes
* conn; you must call connection_add() to link it into the main array.
*
- * Set conn-\>type to <b>type</b>. Set conn-\>s and conn-\>poll_index to
+ * Set conn-\>type to <b>type</b>. Set conn-\>s and conn-\>conn_array_index to
* -1 to signify they are not yet assigned.
*
* If conn is not a listener type, allocate buffers for it. If it's
@@ -157,15 +162,40 @@ conn_state_to_string(int type, int state)
connection_t *
connection_new(int type)
{
- static uint32_t n_connections_allocated = 0;
+ static uint32_t n_connections_allocated = 1;
connection_t *conn;
time_t now = time(NULL);
+ size_t length;
+ uint32_t magic;
- conn = tor_malloc_zero(sizeof(connection_t));
- conn->magic = CONNECTION_MAGIC;
+ switch (type) {
+ case CONN_TYPE_OR:
+ length = sizeof(or_connection_t);
+ magic = OR_CONNECTION_MAGIC;
+ break;
+ case CONN_TYPE_EXIT:
+ case CONN_TYPE_AP:
+ length = sizeof(edge_connection_t);
+ magic = EDGE_CONNECTION_MAGIC;
+ break;
+ case CONN_TYPE_DIR:
+ length = sizeof(dir_connection_t);
+ magic = DIR_CONNECTION_MAGIC;
+ break;
+ case CONN_TYPE_CONTROL:
+ length = sizeof(control_connection_t);
+ magic = CONTROL_CONNECTION_MAGIC;
+ break;
+ default:
+ length = sizeof(connection_t);
+ magic = BASE_CONNECTION_MAGIC;
+ break;
+ }
+
+ conn = tor_malloc_zero(length);
+ conn->magic = magic;
conn->s = -1; /* give it a default of 'not used' */
- conn->poll_index = -1; /* also default to 'not used' */
- conn->global_identifier = n_connections_allocated++;
+ conn->conn_array_index = -1; /* also default to 'not used' */
conn->type = type;
if (!connection_is_listener(conn)) { /* listeners never use their buf */
@@ -173,10 +203,14 @@ connection_new(int type)
conn->outbuf = buf_new();
}
if (type == CONN_TYPE_AP) {
- conn->socks_request = tor_malloc_zero(sizeof(socks_request_t));
+ TO_EDGE_CONN(conn)->socks_request =
+ tor_malloc_zero(sizeof(socks_request_t));
}
-
- conn->next_circ_id = crypto_rand_int(1<<15);
+ if (CONN_IS_EDGE(conn)) {
+ TO_EDGE_CONN(conn)->global_identifier = n_connections_allocated++;
+ }
+ if (type == CONN_TYPE_OR)
+ TO_OR_CONN(conn)->next_circ_id = crypto_rand_int(1<<15);
conn->timestamp_created = now;
conn->timestamp_lastread = now;
@@ -208,30 +242,72 @@ connection_unregister(connection_t *conn)
static void
_connection_free(connection_t *conn)
{
- tor_assert(conn->magic == CONNECTION_MAGIC);
+ void *mem;
+ switch (conn->type) {
+ case CONN_TYPE_OR:
+ tor_assert(conn->magic == OR_CONNECTION_MAGIC);
+ mem = TO_OR_CONN(conn);
+ break;
+ case CONN_TYPE_AP:
+ case CONN_TYPE_EXIT:
+ tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
+ mem = TO_EDGE_CONN(conn);
+ break;
+ case CONN_TYPE_DIR:
+ tor_assert(conn->magic == DIR_CONNECTION_MAGIC);
+ mem = TO_DIR_CONN(conn);
+ break;
+ case CONN_TYPE_CONTROL:
+ tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
+ mem = TO_CONTROL_CONN(conn);
+ break;
+ default:
+ tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
+ mem = conn;
+ break;
+ }
if (!connection_is_listener(conn)) {
buf_free(conn->inbuf);
buf_free(conn->outbuf);
}
+
tor_free(conn->address);
- tor_free(conn->chosen_exit_name);
if (connection_speaks_cells(conn)) {
- if (conn->tls) {
- tor_tls_free(conn->tls);
- conn->tls = NULL;
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ if (or_conn->tls) {
+ tor_tls_free(or_conn->tls);
+ or_conn->tls = NULL;
}
+
+ tor_free(or_conn->nickname);
+ }
+ if (CONN_IS_EDGE(conn)) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ tor_free(edge_conn->chosen_exit_name);
+ tor_free(edge_conn->socks_request);
+ }
+ if (conn->type == CONN_TYPE_CONTROL) {
+ control_connection_t *control_conn = TO_CONTROL_CONN(conn);
+ tor_free(control_conn->incoming_cmd);
}
- if (conn->identity_pkey)
- crypto_free_pk_env(conn->identity_pkey);
- tor_free(conn->nickname);
- tor_free(conn->socks_request);
- tor_free(conn->incoming_cmd);
tor_free(conn->read_event); /* Probably already freed by connection_free. */
tor_free(conn->write_event); /* Probably already freed by connection_free. */
- tor_free(conn->requested_resource);
+
+ if (conn->type == CONN_TYPE_DIR) {
+ dir_connection_t *dir_conn = TO_DIR_CONN(conn);
+ tor_free(dir_conn->requested_resource);
+ if (dir_conn->zlib_state)
+ tor_zlib_free(dir_conn->zlib_state);
+ if (dir_conn->fingerprint_stack) {
+ SMARTLIST_FOREACH(dir_conn->fingerprint_stack, char *, cp, tor_free(cp));
+ smartlist_free(dir_conn->fingerprint_stack);
+ }
+ if (dir_conn->cached_dir)
+ cached_dir_decref(dir_conn->cached_dir);
+ }
if (conn->s >= 0) {
log_debug(LD_NET,"closing fd %d.",conn->s);
@@ -239,13 +315,13 @@ _connection_free(connection_t *conn)
}
if (conn->type == CONN_TYPE_OR &&
- !tor_digest_is_zero(conn->identity_digest)) {
+ !tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) {
log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
- connection_or_remove_from_identity_map(conn);
+ connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
memset(conn, 0xAA, sizeof(connection_t)); /* poison memory */
- tor_free(conn);
+ tor_free(mem);
}
/** Make sure <b>conn</b> isn't in any of the global conn lists; then free it.
@@ -259,12 +335,12 @@ connection_free(connection_t *conn)
if (connection_speaks_cells(conn)) {
if (conn->state == OR_CONN_STATE_OPEN)
directory_set_dirty();
- if (!tor_digest_is_zero(conn->identity_digest)) {
- connection_or_remove_from_identity_map(conn);
+ if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest)) {
+ connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
}
if (conn->type == CONN_TYPE_CONTROL) {
- conn->event_mask = 0;
+ TO_CONTROL_CONN(conn)->event_mask = 0;
control_update_global_event_mask();
}
connection_unregister(conn);
@@ -290,7 +366,7 @@ connection_free_all(void)
/* We don't want to log any messages to controllers. */
for (i=0;i<n;i++)
if (carray[i]->type == CONN_TYPE_CONTROL)
- carray[i]->event_mask = 0;
+ TO_CONTROL_CONN(carray[i])->event_mask = 0;
control_update_global_event_mask();
/* Unlink everything from the identity map. */
@@ -319,11 +395,14 @@ void
connection_about_to_close_connection(connection_t *conn)
{
circuit_t *circ;
+ dir_connection_t *dir_conn;
+ or_connection_t *or_conn;
+ edge_connection_t *edge_conn;
assert(conn->marked_for_close);
if (CONN_IS_EDGE(conn)) {
- if (!conn->has_sent_end) {
+ if (!conn->edge_has_sent_end) {
log_warn(LD_BUG, "Harmless bug: Edge connection (marked at %s:%d) "
"hasn't sent end yet?",
conn->marked_for_close_file, conn->marked_for_close);
@@ -333,23 +412,25 @@ connection_about_to_close_connection(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_DIR:
+ dir_conn = TO_DIR_CONN(conn);
if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) {
/* It's a directory connection and connecting or fetching
* failed: forget about this router, and maybe try again. */
- connection_dir_request_failed(conn);
+ connection_dir_request_failed(dir_conn);
// XXX if it's rend desc we may want to retry -RD
}
if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC)
- rend_client_desc_here(conn->rend_query); /* give it a try */
+ rend_client_desc_here(dir_conn->rend_query); /* give it a try */
break;
case CONN_TYPE_OR:
+ or_conn = TO_OR_CONN(conn);
/* Remember why we're closing this connection. */
if (conn->state != OR_CONN_STATE_OPEN) {
- if (connection_or_nonopen_was_started_here(conn)) {
- rep_hist_note_connect_failed(conn->identity_digest, time(NULL));
- entry_guard_set_status(conn->identity_digest, 0);
- router_set_status(conn->identity_digest, 0);
- control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
+ if (connection_or_nonopen_was_started_here(or_conn)) {
+ rep_hist_note_connect_failed(or_conn->identity_digest, time(NULL));
+ entry_guard_set_status(or_conn->identity_digest, 0);
+ router_set_status(or_conn->identity_digest, 0);
+ control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED);
}
} else if (conn->hold_open_until_flushed) {
/* XXXX009 We used to have an arg that told us whether we closed the
@@ -361,30 +442,32 @@ connection_about_to_close_connection(connection_t *conn)
* flushing still get noted as dead, not disconnected. But this is an
* improvement. -NM
*/
- rep_hist_note_disconnect(conn->identity_digest, time(NULL));
- control_event_or_conn_status(conn, OR_CONN_EVENT_CLOSED);
- } else if (conn->identity_digest) {
- rep_hist_note_connection_died(conn->identity_digest, time(NULL));
- control_event_or_conn_status(conn, OR_CONN_EVENT_CLOSED);
+ rep_hist_note_disconnect(or_conn->identity_digest, time(NULL));
+ control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED);
+ } else if (or_conn->identity_digest) {
+ rep_hist_note_connection_died(or_conn->identity_digest, time(NULL));
+ control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED);
}
break;
case CONN_TYPE_AP:
- if (conn->socks_request->has_finished == 0) {
+ edge_conn = TO_EDGE_CONN(conn);
+ if (edge_conn->socks_request->has_finished == 0) {
/* since conn gets removed right after this function finishes,
* there's no point trying to send back a reply at this point. */
log_warn(LD_BUG,"Bug: Closing stream (marked at %s:%d) without sending"
" back a socks reply.",
conn->marked_for_close_file, conn->marked_for_close);
} else {
- control_event_stream_status(conn, STREAM_EVENT_CLOSED);
+ control_event_stream_status(edge_conn, STREAM_EVENT_CLOSED);
}
break;
case CONN_TYPE_EXIT:
+ edge_conn = TO_EDGE_CONN(conn);
if (conn->state == EXIT_CONN_STATE_RESOLVING) {
- circ = circuit_get_by_edge_conn(conn);
+ circ = circuit_get_by_edge_conn(edge_conn);
if (circ)
- circuit_detach_stream(circ, conn);
- connection_dns_remove(conn);
+ circuit_detach_stream(circ, edge_conn);
+ connection_dns_remove(edge_conn);
}
break;
case CONN_TYPE_DNSWORKER:
@@ -515,7 +598,7 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
#endif
memset(&listenaddr,0,sizeof(struct sockaddr_in));
- if (parse_addr_port(listenaddress, &address, &addr, &usePort)<0) {
+ if (parse_addr_port(LOG_WARN, listenaddress, &address, &addr, &usePort)<0) {
log_warn(LD_CONFIG,
"Error parsing/resolving ListenAddress %s", listenaddress);
return NULL;
@@ -534,10 +617,6 @@ connection_create_listener(const char *listenaddress, uint16_t listenport,
if (s < 0) {
log_warn(LD_NET,"Socket creation failed.");
goto err;
- } else if (!SOCKET_IS_POLLABLE(s)) {
- log_warn(LD_NET,"Too many connections; can't create pollable listener.");
- tor_close_socket(s);
- goto err;
}
#ifndef MS_WINDOWS
@@ -637,16 +716,8 @@ connection_handle_listener_read(connection_t *conn, int new_type)
memset(addrbuf, 0, sizeof(addrbuf));
news = accept(conn->s,(struct sockaddr *)&addrbuf,&remotelen);
- if (!SOCKET_IS_POLLABLE(news)) {
- /* accept() error, or too many conns to poll */
- int e;
- if (news>=0) {
- /* Too many conns to poll. */
- log_warn(LD_NET,"Too many connections; couldn't accept connection.");
- tor_close_socket(news);
- return 0;
- }
- e = tor_socket_errno(conn->s);
+ if (news < 0) { /* accept() error */
+ int e = tor_socket_errno(conn->s);
if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
return 0; /* he hung up before we could accept(). that's fine. */
} else if (ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e)) {
@@ -719,7 +790,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
return 0; /* no need to tear down the parent */
}
- if (connection_init_accepted_conn(newconn) < 0) {
+ if (connection_init_accepted_conn(newconn, conn->type) < 0) {
connection_mark_for_close(newconn);
return 0;
}
@@ -730,16 +801,23 @@ connection_handle_listener_read(connection_t *conn, int new_type)
* If conn is an OR, start the tls handshake.
*/
static int
-connection_init_accepted_conn(connection_t *conn)
+connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
{
connection_start_reading(conn);
switch (conn->type) {
case CONN_TYPE_OR:
- control_event_or_conn_status(conn, OR_CONN_EVENT_NEW);
- return connection_tls_start_handshake(conn, 1);
+ control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW);
+ return connection_tls_start_handshake(TO_OR_CONN(conn), 1);
case CONN_TYPE_AP:
- conn->state = AP_CONN_STATE_SOCKS_WAIT;
+ switch (listener_type) {
+ case CONN_TYPE_AP_LISTENER:
+ conn->state = AP_CONN_STATE_SOCKS_WAIT;
+ break;
+ case CONN_TYPE_AP_TRANS_LISTENER:
+ conn->state = AP_CONN_STATE_ORIGDST_WAIT;
+ break;
+ }
break;
case CONN_TYPE_DIR:
conn->purpose = DIR_PURPOSE_SERVER;
@@ -773,12 +851,6 @@ connection_connect(connection_t *conn, char *address,
log_warn(LD_NET,"Error creating network socket: %s",
tor_socket_strerror(tor_socket_errno(-1)));
return -1;
- } else if (!SOCKET_IS_POLLABLE(s)) {
- log_warn(LD_NET,
- "Too many connections; can't create pollable connection to %s",
- escaped_safe_str(address));
- tor_close_socket(s);
- return -1;
}
if (options->OutboundBindAddress) {
@@ -903,7 +975,7 @@ retry_listeners(int type, config_line_t *cfg,
{
char *address=NULL;
uint16_t port;
- if (! parse_addr_port(wanted->value, &address, NULL, &port)) {
+ if (!parse_addr_port(LOG_WARN, wanted->value, &address, NULL, &port)) {
int addr_matches = !strcasecmp(address, conn->address);
tor_free(address);
if (! port)
@@ -985,6 +1057,10 @@ retry_all_listeners(int force, smartlist_t *replaced_conns,
options->SocksPort, "127.0.0.1", force,
replaced_conns, new_conns, 0)<0)
return -1;
+ if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
+ options->TransPort, "127.0.0.1", force,
+ replaced_conns, new_conns, 0)<0)
+ return -1;
if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
options->ControlListenAddress,
options->ControlPort, "127.0.0.1", force,
@@ -1017,9 +1093,11 @@ connection_bucket_read_limit(connection_t *conn)
if (at_most > global_read_bucket)
at_most = global_read_bucket;
- if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN)
- if (at_most > conn->receiver_bucket)
- at_most = conn->receiver_bucket;
+ if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ if (at_most > or_conn->receiver_bucket)
+ at_most = or_conn->receiver_bucket;
+ }
if (at_most < 0)
return 0;
@@ -1062,10 +1140,8 @@ static void
connection_read_bucket_decrement(connection_t *conn, int num_read)
{
global_read_bucket -= num_read;
- //tor_assert(global_read_bucket >= 0);
if (connection_speaks_cells(conn) && conn->state == OR_CONN_STATE_OPEN) {
- conn->receiver_bucket -= num_read;
- //tor_assert(conn->receiver_bucket >= 0);
+ TO_OR_CONN(conn)->receiver_bucket -= num_read;
}
}
@@ -1082,7 +1158,7 @@ connection_consider_empty_buckets(connection_t *conn)
}
if (connection_speaks_cells(conn) &&
conn->state == OR_CONN_STATE_OPEN &&
- conn->receiver_bucket <= 0) {
+ TO_OR_CONN(conn)->receiver_bucket <= 0) {
LOG_FN_CONN(conn,
(LOG_DEBUG,LD_NET,"receiver bucket exhausted. Pausing."));
conn->wants_to_read = 1;
@@ -1090,8 +1166,7 @@ connection_consider_empty_buckets(connection_t *conn)
}
}
-/** Initialize the global read bucket to options->BandwidthBurst,
- * and current_time to the current time. */
+/** Initialize the global read bucket to options->BandwidthBurst. */
void
connection_bucket_init(void)
{
@@ -1109,6 +1184,9 @@ connection_bucket_refill(struct timeval *now)
connection_t *conn;
connection_t **carray;
or_options_t *options = get_options();
+ /* Not used, but it should be! We might have rolled over more than one
+ * second! XXXX */
+ (void) now;
/* refill the global buckets */
if (global_read_bucket < (int)options->BandwidthBurst) {
@@ -1125,10 +1203,15 @@ connection_bucket_refill(struct timeval *now)
for (i=0;i<n;i++) {
conn = carray[i];
- if (connection_receiver_bucket_should_increase(conn)) {
- conn->receiver_bucket = conn->bandwidth;
- //log_fn(LOG_DEBUG,"Receiver bucket %d now %d.", i,
- // conn->receiver_bucket);
+ if (connection_speaks_cells(conn)) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ if (connection_receiver_bucket_should_increase(or_conn)) {
+ or_conn->receiver_bucket += or_conn->bandwidthrate;
+ if (or_conn->receiver_bucket > or_conn->bandwidthburst)
+ or_conn->receiver_bucket = or_conn->bandwidthburst;
+ //log_fn(LOG_DEBUG,"Receiver bucket %d now %d.", i,
+ // conn->receiver_bucket);
+ }
}
if (conn->wants_to_read == 1 /* it's marked to turn reading back on now */
@@ -1137,7 +1220,7 @@ connection_bucket_refill(struct timeval *now)
* not the best place to check this.) */
&& (!connection_speaks_cells(conn) ||
conn->state != OR_CONN_STATE_OPEN ||
- conn->receiver_bucket > 0)) {
+ TO_OR_CONN(conn)->receiver_bucket > 0)) {
/* and either a non-cell conn or a cell conn with non-empty bucket */
LOG_FN_CONN(conn, (LOG_DEBUG,LD_NET,"waking up conn (fd %d)",conn->s));
conn->wants_to_read = 0;
@@ -1154,16 +1237,13 @@ connection_bucket_refill(struct timeval *now)
* should add another pile of tokens to it?
*/
static int
-connection_receiver_bucket_should_increase(connection_t *conn)
+connection_receiver_bucket_should_increase(or_connection_t *conn)
{
tor_assert(conn);
- if (!connection_speaks_cells(conn))
- return 0; /* edge connections don't use receiver_buckets */
- if (conn->state != OR_CONN_STATE_OPEN)
+ if (conn->_base.state != OR_CONN_STATE_OPEN)
return 0; /* only open connections play the rate limiting game */
-
- if (conn->receiver_bucket >= conn->bandwidth)
+ if (conn->receiver_bucket >= conn->bandwidthburst)
return 0;
return 1;
@@ -1195,6 +1275,7 @@ connection_handle_read(connection_t *conn)
case CONN_TYPE_OR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_OR);
case CONN_TYPE_AP_LISTENER:
+ case CONN_TYPE_AP_TRANS_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_AP);
case CONN_TYPE_DIR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_DIR);
@@ -1209,15 +1290,15 @@ loop_again:
/* There's a read error; kill the connection.*/
connection_close_immediate(conn); /* Don't flush; connection is dead. */
if (CONN_IS_EDGE(conn)) {
- connection_edge_end_errno(conn, conn->cpath_layer);
- if (conn->socks_request) /* broken, so don't send a socks reply back */
- conn->socks_request->has_finished = 1;
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ connection_edge_end_errno(edge_conn, edge_conn->cpath_layer);
+ if (edge_conn->socks_request) /* broken, don't send a socks reply back */
+ edge_conn->socks_request->has_finished = 1;
}
connection_mark_for_close(conn);
return -1;
}
- if (CONN_IS_EDGE(conn) &&
- try_to_read != max_to_read) {
+ if (CONN_IS_EDGE(conn) && try_to_read != max_to_read) {
/* instruct it not to try to package partial cells. */
if (connection_process_inbuf(conn, 0) < 0) {
return -1;
@@ -1274,29 +1355,32 @@ connection_read_to_buf(connection_t *conn, int *max_to_read)
if (connection_speaks_cells(conn) &&
conn->state > OR_CONN_STATE_PROXY_READING) {
int pending;
+ or_connection_t *or_conn = TO_OR_CONN(conn);
if (conn->state == OR_CONN_STATE_HANDSHAKING) {
/* continue handshaking even if global token bucket is empty */
- return connection_tls_continue_handshake(conn);
+ return connection_tls_continue_handshake(or_conn);
}
log_debug(LD_NET,
"%d: starting, inbuf_datalen %d (%d pending in tls object)."
" at_most %d.",
conn->s,(int)buf_datalen(conn->inbuf),
- tor_tls_get_pending_bytes(conn->tls), at_most);
+ tor_tls_get_pending_bytes(or_conn->tls), at_most);
/* else open, or closing */
- result = read_to_buf_tls(conn->tls, at_most, conn->inbuf);
+ result = read_to_buf_tls(or_conn->tls, at_most, conn->inbuf);
switch (result) {
case TOR_TLS_CLOSE:
log_info(LD_NET,"TLS connection closed on read. Closing. "
"(Nickname %s, address %s",
- conn->nickname ? conn->nickname : "not set", conn->address);
+ or_conn->nickname ? or_conn->nickname : "not set",
+ conn->address);
return -1;
case TOR_TLS_ERROR:
log_info(LD_NET,"tls error. breaking (nickname %s, address %s).",
- conn->nickname ? conn->nickname : "not set", conn->address);
+ or_conn->nickname ? or_conn->nickname : "not set",
+ conn->address);
return -1;
case TOR_TLS_WANTWRITE:
connection_start_writing(conn);
@@ -1308,12 +1392,12 @@ connection_read_to_buf(connection_t *conn, int *max_to_read)
default:
break;
}
- pending = tor_tls_get_pending_bytes(conn->tls);
+ pending = tor_tls_get_pending_bytes(or_conn->tls);
if (pending) {
/* XXXX If we have any pending bytes, read them now. This *can*
* take us over our read allotment, but really we shouldn't be
* believing that SSL bytes are the same as TCP bytes anyway. */
- int r2 = read_to_buf_tls(conn->tls, pending, conn->inbuf);
+ int r2 = read_to_buf_tls(or_conn->tls, pending, conn->inbuf);
if (r2<0) {
log_warn(LD_BUG, "Bug: apparently, reading pending bytes can fail.");
return -1;
@@ -1323,9 +1407,11 @@ connection_read_to_buf(connection_t *conn, int *max_to_read)
}
} else {
+ int reached_eof = 0;
CONN_LOG_PROTECT(conn,
- result = read_to_buf(conn->s, at_most, conn->inbuf,
- &conn->inbuf_reached_eof));
+ result = read_to_buf(conn->s, at_most, conn->inbuf, &reached_eof));
+ if (reached_eof)
+ conn->inbuf_reached_eof = 1;
// log_fn(LOG_DEBUG,"read_to_buf returned %d.",read_result);
@@ -1418,7 +1504,8 @@ connection_handle_write(connection_t *conn)
log_warn(LD_BUG,
"getsockopt() syscall failed?! Please report to tor-ops.");
if (CONN_IS_EDGE(conn))
- connection_edge_end_errno(conn, conn->cpath_layer);
+ connection_edge_end_errno(TO_EDGE_CONN(conn),
+ TO_EDGE_CONN(conn)->cpath_layer);
connection_mark_for_close(conn);
return -1;
}
@@ -1427,7 +1514,8 @@ connection_handle_write(connection_t *conn)
if (!ERRNO_IS_CONN_EINPROGRESS(e)) {
log_info(LD_NET,"in-progress connect failed. Removing.");
if (CONN_IS_EDGE(conn))
- connection_edge_end_errno(conn, conn->cpath_layer);
+ connection_edge_end_errno(TO_EDGE_CONN(conn),
+ TO_EDGE_CONN(conn)->cpath_layer);
connection_close_immediate(conn);
connection_mark_for_close(conn);
@@ -1435,7 +1523,7 @@ connection_handle_write(connection_t *conn)
* ignores unrecognized routers
*/
if (conn->type == CONN_TYPE_OR && !get_options()->HttpsProxy)
- router_set_status(conn->identity_digest, 0);
+ router_set_status(TO_OR_CONN(conn)->identity_digest, 0);
return -1;
} else {
return 0; /* no change, see if next time is better */
@@ -1450,9 +1538,10 @@ connection_handle_write(connection_t *conn)
if (connection_speaks_cells(conn) &&
conn->state > OR_CONN_STATE_PROXY_READING) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
if (conn->state == OR_CONN_STATE_HANDSHAKING) {
connection_stop_writing(conn);
- if (connection_tls_continue_handshake(conn) < 0) {
+ if (connection_tls_continue_handshake(or_conn) < 0) {
/* Don't flush; connection is dead. */
connection_close_immediate(conn);
connection_mark_for_close(conn);
@@ -1462,7 +1551,7 @@ connection_handle_write(connection_t *conn)
}
/* else open, or closing */
- result = flush_buf_tls(conn->tls, conn->outbuf,
+ result = flush_buf_tls(or_conn->tls, conn->outbuf,
max_to_write, &conn->outbuf_flushlen);
switch (result) {
case TOR_TLS_ERROR:
@@ -1500,7 +1589,8 @@ connection_handle_write(connection_t *conn)
max_to_write, &conn->outbuf_flushlen));
if (result < 0) {
if (CONN_IS_EDGE(conn))
- connection_edge_end_errno(conn, conn->cpath_layer);
+ connection_edge_end_errno(TO_EDGE_CONN(conn),
+ TO_EDGE_CONN(conn)->cpath_layer);
connection_close_immediate(conn); /* Don't flush; connection is dead. */
connection_mark_for_close(conn);
@@ -1508,9 +1598,13 @@ connection_handle_write(connection_t *conn)
}
}
- if (result > 0 && !is_local_IP(conn->addr)) { /* remember it */
- rep_hist_note_bytes_written(result, now);
- global_write_bucket -= result;
+ if (result > 0) {
+ if (!is_local_IP(conn->addr)) { /* remember it */
+ rep_hist_note_bytes_written(result, time(NULL));
+ global_write_bucket -= result;
+ }
+ if (connection_flushed_some(conn) < 0)
+ connection_mark_for_close(conn);
}
if (!connection_wants_to_flush(conn)) { /* it's done flushing */
@@ -1526,16 +1620,17 @@ connection_handle_write(connection_t *conn)
/* A controller event has just happened with such urgency that we
* need to write it onto controller <b>conn</b> immediately. */
void
-_connection_controller_force_write(connection_t *conn)
+_connection_controller_force_write(control_connection_t *control_conn)
{
/* XXX This is hideous code duplication, but raising it seems a little
* tricky for now. Think more about this one. We only call it for
* EVENT_ERR_MSG, so messing with buckets a little isn't such a big problem.
*/
int result;
- tor_assert(conn);
- tor_assert(!conn->tls);
- tor_assert(conn->type == CONN_TYPE_CONTROL);
+ connection_t *conn;
+ tor_assert(control_conn);
+ conn = TO_CONN(control_conn);
+
if (conn->marked_for_close || conn->s < 0)
return;
@@ -1548,9 +1643,13 @@ _connection_controller_force_write(connection_t *conn)
return;
}
- if (result > 0 && !is_local_IP(conn->addr)) { /* remember it */
- rep_hist_note_bytes_written(result, time(NULL));
- global_write_bucket -= result;
+ if (result > 0) {
+ if (!is_local_IP(conn->addr)) { /* remember it */
+ rep_hist_note_bytes_written(result, time(NULL));
+ global_write_bucket -= result;
+ }
+ if (connection_flushed_some(conn) < 0)
+ connection_mark_for_close(conn);
}
if (!connection_wants_to_flush(conn)) { /* it's done flushing */
@@ -1581,7 +1680,7 @@ connection_write_to_buf(const char *string, size_t len, connection_t *conn)
wrong compared to our max outbuf size. close the whole circuit. */
log_warn(LD_NET,
"write_to_buf failed. Closing circuit (fd %d).", conn->s);
- circuit_mark_for_close(circuit_get_by_edge_conn(conn),
+ circuit_mark_for_close(circuit_get_by_edge_conn(TO_EDGE_CONN(conn)),
END_CIRC_REASON_INTERNAL);
} else {
log_warn(LD_NET,
@@ -1595,13 +1694,45 @@ connection_write_to_buf(const char *string, size_t len, connection_t *conn)
conn->outbuf_flushlen += len;
}
+void
+connection_write_to_buf_zlib(dir_connection_t *dir_conn,
+ const char *data, size_t data_len,
+ int done)
+{
+ int r;
+ size_t old_datalen;
+ connection_t *conn;
+ if (!data_len)
+ return;
+ conn = TO_CONN(dir_conn);
+ /* if it's marked for close, only allow write if we mean to flush it */
+ if (conn->marked_for_close && !conn->hold_open_until_flushed)
+ return;
+
+ old_datalen = buf_datalen(conn->outbuf);
+ /* XXXX TOO much duplicate code! XXXX012NM */
+ CONN_LOG_PROTECT(conn, r = write_to_buf_zlib(
+ conn->outbuf, dir_conn->zlib_state,
+ data, data_len, done));
+ if (r < 0) {
+ log_warn(LD_NET,
+ "write_to_buf failed. Closing connection (fd %d).", conn->s);
+ connection_mark_for_close(conn);
+ return;
+ }
+
+ connection_start_writing(conn);
+ conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen;
+}
+
/** Return the conn to addr/port that has the most recent
* timestamp_created, or NULL if no such conn exists. */
-connection_t *
+or_connection_t *
connection_or_exact_get_by_addr_port(uint32_t addr, uint16_t port)
{
int i, n;
- connection_t *conn, *best=NULL;
+ connection_t *conn;
+ or_connection_t *best=NULL;
connection_t **carray;
get_connection_array(&carray,&n);
@@ -1611,8 +1742,8 @@ connection_or_exact_get_by_addr_port(uint32_t addr, uint16_t port)
conn->addr == addr &&
conn->port == port &&
!conn->marked_for_close &&
- (!best || best->timestamp_created < conn->timestamp_created))
- best = conn;
+ (!best || best->_base.timestamp_created < conn->timestamp_created))
+ best = TO_OR_CONN(conn);
}
return best;
}
@@ -1644,7 +1775,7 @@ connection_get_by_type_addr_port_purpose(int type,
/** Return the connection with id <b>id</b> if it is not already marked for
* close.
*/
-connection_t *
+edge_connection_t *
connection_get_by_global_id(uint32_t id)
{
int i, n;
@@ -1654,9 +1785,9 @@ connection_get_by_global_id(uint32_t id)
get_connection_array(&carray,&n);
for (i=0;i<n;i++) {
conn = carray[i];
- if (conn->global_identifier == id) {
+ if (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->global_identifier == id) {
if (!conn->marked_for_close)
- return conn;
+ return TO_EDGE_CONN(conn);
else
return NULL;
}
@@ -1734,14 +1865,22 @@ connection_get_by_type_state_rendquery(int type, int state,
connection_t *conn;
connection_t **carray;
+ tor_assert(type == CONN_TYPE_DIR ||
+ type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
+
get_connection_array(&carray,&n);
for (i=0;i<n;i++) {
conn = carray[i];
if (conn->type == type &&
!conn->marked_for_close &&
- (!state || state == conn->state) &&
- !rend_cmp_service_ids(rendquery, conn->rend_query))
- return conn;
+ (!state || state == conn->state)) {
+ if (type == CONN_TYPE_DIR &&
+ rend_cmp_service_ids(rendquery, TO_DIR_CONN(conn)->rend_query))
+ return conn;
+ else if (CONN_IS_EDGE(conn) &&
+ rend_cmp_service_ids(rendquery, TO_EDGE_CONN(conn)->rend_query))
+ return conn;
+ }
}
return NULL;
}
@@ -1772,6 +1911,7 @@ connection_is_listener(connection_t *conn)
{
if (conn->type == CONN_TYPE_OR_LISTENER ||
conn->type == CONN_TYPE_AP_LISTENER ||
+ conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
conn->type == CONN_TYPE_DIR_LISTENER ||
conn->type == CONN_TYPE_CONTROL_LISTENER)
return 1;
@@ -1859,7 +1999,7 @@ client_check_address_changed(int sock)
uint32_t *ip;
if (!last_interface_ip)
- get_interface_address(&last_interface_ip);
+ get_interface_address(LOG_INFO, &last_interface_ip);
if (!outgoing_addrs)
outgoing_addrs = smartlist_create();
@@ -1877,7 +2017,7 @@ client_check_address_changed(int sock)
/* Uh-oh. We haven't connected from this address before. Has the interface
* address changed? */
- if (get_interface_address(&iface_ip)<0)
+ if (get_interface_address(LOG_INFO, &iface_ip)<0)
return;
ip = tor_malloc(sizeof(uint32_t));
*ip = ip_out;
@@ -1911,18 +2051,19 @@ connection_process_inbuf(connection_t *conn, int package_partial)
switch (conn->type) {
case CONN_TYPE_OR:
- return connection_or_process_inbuf(conn);
+ return connection_or_process_inbuf(TO_OR_CONN(conn));
case CONN_TYPE_EXIT:
case CONN_TYPE_AP:
- return connection_edge_process_inbuf(conn, package_partial);
+ return connection_edge_process_inbuf(TO_EDGE_CONN(conn),
+ package_partial);
case CONN_TYPE_DIR:
- return connection_dir_process_inbuf(conn);
+ return connection_dir_process_inbuf(TO_DIR_CONN(conn));
case CONN_TYPE_DNSWORKER:
return connection_dns_process_inbuf(conn);
case CONN_TYPE_CPUWORKER:
return connection_cpu_process_inbuf(conn);
case CONN_TYPE_CONTROL:
- return connection_control_process_inbuf(conn);
+ return connection_control_process_inbuf(TO_CONTROL_CONN(conn));
default:
log_err(LD_BUG,"Bug: got unexpected conn type %d.", conn->type);
tor_fragile_assert();
@@ -1930,6 +2071,17 @@ connection_process_inbuf(connection_t *conn, int package_partial)
}
}
+/** Called whenever we've written data on a connection. */
+static int
+connection_flushed_some(connection_t *conn)
+{
+ if (conn->type == CONN_TYPE_DIR &&
+ conn->state == DIR_CONN_STATE_SERVER_WRITING)
+ return connection_dirserv_flushed_some(TO_DIR_CONN(conn));
+ else
+ return 0;
+}
+
/** We just finished flushing bytes from conn-\>outbuf, and there
* are no more bytes remaining.
*
@@ -1945,18 +2097,18 @@ connection_finished_flushing(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR:
- return connection_or_finished_flushing(conn);
+ return connection_or_finished_flushing(TO_OR_CONN(conn));
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
- return connection_edge_finished_flushing(conn);
+ return connection_edge_finished_flushing(TO_EDGE_CONN(conn));
case CONN_TYPE_DIR:
- return connection_dir_finished_flushing(conn);
+ return connection_dir_finished_flushing(TO_DIR_CONN(conn));
case CONN_TYPE_DNSWORKER:
return connection_dns_finished_flushing(conn);
case CONN_TYPE_CPUWORKER:
return connection_cpu_finished_flushing(conn);
case CONN_TYPE_CONTROL:
- return connection_control_finished_flushing(conn);
+ return connection_control_finished_flushing(TO_CONTROL_CONN(conn));
default:
log_err(LD_BUG,"Bug: got unexpected conn type %d.", conn->type);
tor_fragile_assert();
@@ -1977,11 +2129,11 @@ connection_finished_connecting(connection_t *conn)
switch (conn->type)
{
case CONN_TYPE_OR:
- return connection_or_finished_connecting(conn);
+ return connection_or_finished_connecting(TO_OR_CONN(conn));
case CONN_TYPE_EXIT:
- return connection_edge_finished_connecting(conn);
+ return connection_edge_finished_connecting(TO_EDGE_CONN(conn));
case CONN_TYPE_DIR:
- return connection_dir_finished_connecting(conn);
+ return connection_dir_finished_connecting(TO_DIR_CONN(conn));
default:
log_err(LD_BUG,"Bug: got unexpected conn type %d.", conn->type);
tor_fragile_assert();
@@ -1995,18 +2147,18 @@ connection_reached_eof(connection_t *conn)
{
switch (conn->type) {
case CONN_TYPE_OR:
- return connection_or_reached_eof(conn);
+ return connection_or_reached_eof(TO_OR_CONN(conn));
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
- return connection_edge_reached_eof(conn);
+ return connection_edge_reached_eof(TO_EDGE_CONN(conn));
case CONN_TYPE_DIR:
- return connection_dir_reached_eof(conn);
+ return connection_dir_reached_eof(TO_DIR_CONN(conn));
case CONN_TYPE_DNSWORKER:
return connection_dns_reached_eof(conn);
case CONN_TYPE_CPUWORKER:
return connection_cpu_reached_eof(conn);
case CONN_TYPE_CONTROL:
- return connection_control_reached_eof(conn);
+ return connection_control_reached_eof(TO_CONTROL_CONN(conn));
default:
log_err(LD_BUG,"Bug: got unexpected conn type %d.", conn->type);
tor_fragile_assert();
@@ -2020,10 +2172,28 @@ connection_reached_eof(connection_t *conn)
void
assert_connection_ok(connection_t *conn, time_t now)
{
+ (void) now; /* XXXX unused. */
tor_assert(conn);
- tor_assert(conn->magic == CONNECTION_MAGIC);
tor_assert(conn->type >= _CONN_TYPE_MIN);
tor_assert(conn->type <= _CONN_TYPE_MAX);
+ switch (conn->type) {
+ case CONN_TYPE_OR:
+ tor_assert(conn->magic == OR_CONNECTION_MAGIC);
+ break;
+ case CONN_TYPE_AP:
+ case CONN_TYPE_EXIT:
+ tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
+ break;
+ case CONN_TYPE_DIR:
+ tor_assert(conn->magic == DIR_CONNECTION_MAGIC);
+ break;
+ case CONN_TYPE_CONTROL:
+ tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
+ break;
+ default:
+ tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
+ break;
+ }
if (conn->outbuf_flushlen > 0) {
tor_assert(connection_is_writing(conn) || conn->wants_to_write);
@@ -2032,7 +2202,7 @@ assert_connection_ok(connection_t *conn, time_t now)
if (conn->hold_open_until_flushed)
tor_assert(conn->marked_for_close);
- /* XXX check: wants_to_read, wants_to_write, s, poll_index,
+ /* XXX check: wants_to_read, wants_to_write, s, conn_array_index,
* marked_for_close. */
/* buffers */
@@ -2050,9 +2220,8 @@ assert_connection_ok(connection_t *conn, time_t now)
*/
#endif
- if (conn->type != CONN_TYPE_OR) {
- tor_assert(!conn->tls);
- } else {
+ if (conn->type == CONN_TYPE_OR) {
+ or_connection_t *or_conn = TO_OR_CONN(conn);
if (conn->state == OR_CONN_STATE_OPEN) {
/* tor_assert(conn->bandwidth > 0); */
/* the above isn't necessarily true: if we just did a TLS
@@ -2065,48 +2234,37 @@ assert_connection_ok(connection_t *conn, time_t now)
// tor_assert(conn->addr && conn->port);
tor_assert(conn->address);
if (conn->state > OR_CONN_STATE_PROXY_READING)
- tor_assert(conn->tls);
+ tor_assert(or_conn->tls);
}
- if (! CONN_IS_EDGE(conn)) {
- tor_assert(!conn->stream_id);
- tor_assert(!conn->next_stream);
- tor_assert(!conn->cpath_layer);
- tor_assert(!conn->package_window);
- tor_assert(!conn->deliver_window);
-#if 0
- tor_assert(!conn->done_sending);
- tor_assert(!conn->done_receiving);
-#endif
- } else {
+ if (CONN_IS_EDGE(conn)) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
/* XXX unchecked: package window, deliver window. */
- }
- if (conn->type == CONN_TYPE_AP) {
- tor_assert(conn->socks_request);
- if (conn->state == AP_CONN_STATE_OPEN) {
- tor_assert(conn->socks_request->has_finished);
- if (!conn->marked_for_close) {
- tor_assert(conn->cpath_layer);
- assert_cpath_layer_ok(conn->cpath_layer);
+ if (conn->type == CONN_TYPE_AP) {
+
+ tor_assert(edge_conn->socks_request);
+ if (conn->state == AP_CONN_STATE_OPEN) {
+ tor_assert(edge_conn->socks_request->has_finished);
+ if (!conn->marked_for_close) {
+ tor_assert(edge_conn->cpath_layer);
+ assert_cpath_layer_ok(edge_conn->cpath_layer);
+ }
}
}
- } else {
- tor_assert(!conn->socks_request);
- }
- if (conn->type == CONN_TYPE_EXIT) {
- tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT ||
- conn->purpose == EXIT_PURPOSE_RESOLVE);
+ if (conn->type == CONN_TYPE_EXIT) {
+ tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT ||
+ conn->purpose == EXIT_PURPOSE_RESOLVE);
+ }
} else if (conn->type != CONN_TYPE_DIR) {
- tor_assert(!conn->purpose); /* only used for dir types currently */
- }
- if (conn->type != CONN_TYPE_DIR) {
- tor_assert(!conn->requested_resource);
+ /* Purpose is only used for dir and exit types currently */
+ tor_assert(!conn->purpose);
}
switch (conn->type)
{
case CONN_TYPE_OR_LISTENER:
case CONN_TYPE_AP_LISTENER:
+ case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_DIR_LISTENER:
case CONN_TYPE_CONTROL_LISTENER:
tor_assert(conn->state == LISTENER_STATE_READY);
@@ -2114,7 +2272,7 @@ assert_connection_ok(connection_t *conn, time_t now)
case CONN_TYPE_OR:
tor_assert(conn->state >= _OR_CONN_STATE_MIN);
tor_assert(conn->state <= _OR_CONN_STATE_MAX);
- tor_assert(conn->n_circuits >= 0);
+ tor_assert(TO_OR_CONN(conn)->n_circuits >= 0);
break;
case CONN_TYPE_EXIT:
tor_assert(conn->state >= _EXIT_CONN_STATE_MIN);
@@ -2125,7 +2283,7 @@ assert_connection_ok(connection_t *conn, time_t now)
case CONN_TYPE_AP:
tor_assert(conn->state >= _AP_CONN_STATE_MIN);
tor_assert(conn->state <= _AP_CONN_STATE_MAX);
- tor_assert(conn->socks_request);
+ tor_assert(TO_EDGE_CONN(conn)->socks_request);
break;
case CONN_TYPE_DIR:
tor_assert(conn->state >= _DIR_CONN_STATE_MIN);
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 902e0c009b..9ab672ff07 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -13,25 +13,37 @@ const char connection_edge_c_id[] =
#include "or.h"
+#ifdef HAVE_LINUX_NETFILTER_IPV4_H
+#include <linux/netfilter_ipv4.h>
+#define TRANS_NETFILTER
+#endif
+
+#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
+#include <net/if.h>
+#include <net/pfvar.h>
+#define TRANS_PF
+#endif
+
/* List of exit_redirect_t */
static smartlist_t *redirect_exit_list = NULL;
-static int connection_ap_handshake_process_socks(connection_t *conn);
+static int connection_ap_handshake_process_socks(edge_connection_t *conn);
+static int connection_ap_process_transparent(edge_connection_t *conn);
/** An AP stream has failed/finished. If it hasn't already sent back
* a socks reply, send one now (based on endreason). Also set
* has_sent_end to 1, and mark the conn.
*/
void
-_connection_mark_unattached_ap(connection_t *conn, int endreason,
+_connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
int line, const char *file)
{
- tor_assert(conn->type == CONN_TYPE_AP);
- conn->has_sent_end = 1; /* no circ yet */
+ tor_assert(conn->_base.type == CONN_TYPE_AP);
+ conn->_base.edge_has_sent_end = 1; /* no circ yet */
- if (conn->marked_for_close) {
+ if (conn->_base.marked_for_close) {
/* This call will warn as appropriate. */
- _connection_mark_for_close(conn, line, file);
+ _connection_mark_for_close(TO_CONN(conn), line, file);
return;
}
@@ -51,49 +63,36 @@ _connection_mark_unattached_ap(connection_t *conn, int endreason,
0, NULL, -1);
}
- _connection_mark_for_close(conn, line, file);
- conn->hold_open_until_flushed = 1;
+ _connection_mark_for_close(TO_CONN(conn), line, file);
+ conn->_base.hold_open_until_flushed = 1;
}
/** There was an EOF. Send an end and mark the connection for close.
*/
int
-connection_edge_reached_eof(connection_t *conn)
+connection_edge_reached_eof(edge_connection_t *conn)
{
-#ifdef HALF_OPEN
- /* eof reached; we're done reading, but we might want to write more. */
- conn->done_receiving = 1;
- shutdown(conn->s, 0); /* XXX check return, refactor NM */
- if (conn->done_sending) {
- connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer);
- connection_mark_for_close(conn);
- } else {
- connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
- RELAY_COMMAND_END,
- NULL, 0, conn->cpath_layer);
- }
- return 0;
-#else
- if (buf_datalen(conn->inbuf) && connection_state_is_open(conn)) {
+ if (buf_datalen(conn->_base.inbuf) &&
+ connection_state_is_open(TO_CONN(conn))) {
/* it still has stuff to process. don't let it die yet. */
return 0;
}
- log_info(LD_EDGE,"conn (fd %d) reached eof. Closing.", conn->s);
- if (!conn->marked_for_close) {
+ log_info(LD_EDGE,"conn (fd %d) reached eof. Closing.", conn->_base.s);
+ if (!conn->_base.marked_for_close) {
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
connection_edge_end(conn, END_STREAM_REASON_DONE, conn->cpath_layer);
if (conn->socks_request) /* eof, so don't send a socks reply back */
conn->socks_request->has_finished = 1;
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
}
return 0;
-#endif
}
/** Handle new bytes on conn->inbuf based on state:
* - If it's waiting for socks info, try to read another step of the
* socks handshake out of conn->inbuf.
+ * - If it's waiting for the original destination, fetch it.
* - If it's open, then package more relay cells from the stream.
* - Else, leave the bytes on inbuf alone for now.
*
@@ -101,23 +100,28 @@ connection_edge_reached_eof(connection_t *conn)
* else return 0.
*/
int
-connection_edge_process_inbuf(connection_t *conn, int package_partial)
+connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
{
tor_assert(conn);
- tor_assert(CONN_IS_EDGE(conn));
- switch (conn->state) {
+ switch (conn->_base.state) {
case AP_CONN_STATE_SOCKS_WAIT:
if (connection_ap_handshake_process_socks(conn) < 0) {
/* already marked */
return -1;
}
return 0;
+ case AP_CONN_STATE_ORIGDST_WAIT:
+ if (connection_ap_process_transparent(conn) < 0) {
+ /* already marked */
+ return -1;
+ }
+ return 0;
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
if (connection_edge_package_raw_inbuf(conn, package_partial) < 0) {
/* (We already sent an end cell if possible) */
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return -1;
}
return 0;
@@ -129,13 +133,13 @@ connection_edge_process_inbuf(connection_t *conn, int package_partial)
case AP_CONN_STATE_CONTROLLER_WAIT:
log_info(LD_EDGE,
"data from edge while in '%s' state. Leaving it on buffer.",
- conn_state_to_string(conn->type, conn->state));
+ conn_state_to_string(conn->_base.type, conn->_base.state));
return 0;
}
- log_warn(LD_BUG,"Bug: Got unexpected state %d. Closing.",conn->state);
+ log_warn(LD_BUG,"Bug: Got unexpected state %d. Closing.",conn->_base.state);
tor_fragile_assert();
connection_edge_end(conn, END_STREAM_REASON_INTERNAL, conn->cpath_layer);
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return -1;
}
@@ -143,19 +147,18 @@ connection_edge_process_inbuf(connection_t *conn, int package_partial)
* Mark it for close and return 0.
*/
int
-connection_edge_destroy(uint16_t circ_id, connection_t *conn)
+connection_edge_destroy(uint16_t circ_id, edge_connection_t *conn)
{
- tor_assert(CONN_IS_EDGE(conn));
-
- if (!conn->marked_for_close) {
+ if (!conn->_base.marked_for_close) {
log_info(LD_EDGE,
"CircID %d: At an edge. Marking connection for close.", circ_id);
- if (conn->type == CONN_TYPE_AP) {
+ if (conn->_base.type == CONN_TYPE_AP) {
connection_mark_unattached_ap(conn, END_STREAM_REASON_DESTROY);
} else {
- conn->has_sent_end = 1; /* closing the circuit, nothing to send to */
- connection_mark_for_close(conn);
- conn->hold_open_until_flushed = 1;
+ /* closing the circuit, nothing to send an END to */
+ conn->_base.edge_has_sent_end = 1;
+ connection_mark_for_close(TO_CONN(conn));
+ conn->_base.hold_open_until_flushed = 1;
}
}
conn->cpath_layer = NULL;
@@ -172,45 +175,46 @@ connection_edge_destroy(uint16_t circ_id, connection_t *conn)
* else return 0.
*/
int
-connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer)
+connection_edge_end(edge_connection_t *conn, char reason,
+ crypt_path_t *cpath_layer)
{
char payload[RELAY_PAYLOAD_SIZE];
size_t payload_len=1;
circuit_t *circ;
- if (conn->has_sent_end) {
+ if (conn->_base.edge_has_sent_end) {
log_warn(LD_BUG,"Harmless bug: Calling connection_edge_end (reason %d) "
"on an already ended stream?", reason);
tor_fragile_assert();
return -1;
}
- if (conn->marked_for_close) {
+ if (conn->_base.marked_for_close) {
log_warn(LD_BUG,
"Bug: called on conn that's already marked for close at %s:%d.",
- conn->marked_for_close_file, conn->marked_for_close);
+ conn->_base.marked_for_close_file, conn->_base.marked_for_close);
return 0;
}
payload[0] = reason;
if (reason == END_STREAM_REASON_EXITPOLICY &&
!connection_edge_is_rendezvous_stream(conn)) {
- set_uint32(payload+1, htonl(conn->addr));
- /* XXXX fill with a real TTL! */
- set_uint32(payload+5, htonl(MAX_DNS_ENTRY_AGE));
+ set_uint32(payload+1, htonl(conn->_base.addr));
+ set_uint32(payload+5, htonl(dns_clip_ttl(conn->address_ttl)));
payload_len += 8;
}
circ = circuit_get_by_edge_conn(conn);
if (circ && !circ->marked_for_close) {
- log_debug(LD_EDGE,"Marking conn (fd %d) and sending end.",conn->s);
+ log_debug(LD_EDGE,"Marking conn (fd %d) and sending end.",conn->_base.s);
connection_edge_send_command(conn, circ, RELAY_COMMAND_END,
payload, payload_len, cpath_layer);
} else {
- log_debug(LD_EDGE,"Marking conn (fd %d); no circ to send end.",conn->s);
+ log_debug(LD_EDGE,"Marking conn (fd %d); no circ to send end.",
+ conn->_base.s);
}
- conn->has_sent_end = 1;
+ conn->_base.edge_has_sent_end = 1;
return 0;
}
@@ -219,11 +223,11 @@ connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer)
* an appropriate relay end cell to <b>cpath_layer</b>.
**/
int
-connection_edge_end_errno(connection_t *conn, crypt_path_t *cpath_layer)
+connection_edge_end_errno(edge_connection_t *conn, crypt_path_t *cpath_layer)
{
uint8_t reason;
tor_assert(conn);
- reason = (uint8_t)errno_to_end_reason(tor_socket_errno(conn->s));
+ reason = (uint8_t)errno_to_end_reason(tor_socket_errno(conn->_base.s));
return connection_edge_end(conn, reason, cpath_layer);
}
@@ -238,26 +242,26 @@ connection_edge_end_errno(connection_t *conn, crypt_path_t *cpath_layer)
* return 0.
*/
int
-connection_edge_finished_flushing(connection_t *conn)
+connection_edge_finished_flushing(edge_connection_t *conn)
{
tor_assert(conn);
- tor_assert(CONN_IS_EDGE(conn));
- switch (conn->state) {
+ switch (conn->_base.state) {
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
- connection_stop_writing(conn);
+ connection_stop_writing(TO_CONN(conn));
connection_edge_consider_sending_sendme(conn);
return 0;
case AP_CONN_STATE_SOCKS_WAIT:
+ case AP_CONN_STATE_ORIGDST_WAIT:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
- connection_stop_writing(conn);
+ connection_stop_writing(TO_CONN(conn));
return 0;
default:
- log_warn(LD_BUG,"BUG: called in unexpected state %d.", conn->state);
+ log_warn(LD_BUG,"BUG: called in unexpected state %d.",conn->_base.state);
tor_fragile_assert();
return -1;
}
@@ -268,13 +272,15 @@ connection_edge_finished_flushing(connection_t *conn)
* data, deliver 'CONNECTED' relay cells as appropriate, and check
* any pending data that may have been received. */
int
-connection_edge_finished_connecting(connection_t *conn)
+connection_edge_finished_connecting(edge_connection_t *edge_conn)
{
char valbuf[INET_NTOA_BUF_LEN];
+ connection_t *conn;
struct in_addr in;
- tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_EXIT);
+ tor_assert(edge_conn);
+ tor_assert(edge_conn->_base.type == CONN_TYPE_EXIT);
+ conn = TO_CONN(edge_conn);
tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING);
in.s_addr = htonl(conn->addr);
@@ -288,23 +294,42 @@ connection_edge_finished_connecting(connection_t *conn)
* cells */
connection_start_writing(conn);
/* deliver a 'connected' relay cell back through the circuit. */
- if (connection_edge_is_rendezvous_stream(conn)) {
- if (connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
+ if (connection_edge_is_rendezvous_stream(edge_conn)) {
+ if (connection_edge_send_command(edge_conn,
+ circuit_get_by_edge_conn(edge_conn),
RELAY_COMMAND_CONNECTED, NULL, 0,
- conn->cpath_layer) < 0)
+ edge_conn->cpath_layer) < 0)
return 0; /* circuit is closed, don't continue */
} else {
char connected_payload[8];
set_uint32(connected_payload, htonl(conn->addr));
set_uint32(connected_payload+4,
- htonl(MAX_DNS_ENTRY_AGE)); /* XXXX fill with a real TTL */
- if (connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
- RELAY_COMMAND_CONNECTED, connected_payload, 8, conn->cpath_layer) < 0)
+ htonl(dns_clip_ttl(edge_conn->address_ttl)));
+ if (connection_edge_send_command(edge_conn,
+ circuit_get_by_edge_conn(edge_conn),
+ RELAY_COMMAND_CONNECTED,
+ connected_payload, 8,
+ edge_conn->cpath_layer) < 0)
return 0; /* circuit is closed, don't continue */
}
- tor_assert(conn->package_window > 0);
+ tor_assert(edge_conn->package_window > 0);
/* in case the server has written anything */
- return connection_edge_process_inbuf(conn, 1);
+ return connection_edge_process_inbuf(edge_conn, 1);
+}
+
+/** Define a schedule for how long to wait between retrying
+ * application connections. Rather than waiting a fixed amount of
+ * time between each retry, we wait only 5 seconds for the first,
+ * 10 seconds for the second, and 15 seconds for each retry after
+ * that. Hopefully this will improve the expected user experience. */
+static int
+compute_socks_timeout(edge_connection_t *conn)
+{
+ if (conn->num_socks_retries == 0)
+ return 5;
+ if (conn->num_socks_retries == 1)
+ return 10;
+ return 15;
}
/** Find all general-purpose AP streams waiting for a response that sent their
@@ -320,34 +345,37 @@ void
connection_ap_expire_beginning(void)
{
connection_t **carray;
- connection_t *conn;
+ edge_connection_t *conn;
circuit_t *circ;
const char *nickname;
int n, i;
time_t now = time(NULL);
or_options_t *options = get_options();
int severity;
+ int cutoff;
get_connection_array(&carray, &n);
for (i = 0; i < n; ++i) {
- conn = carray[i];
- if (conn->type != CONN_TYPE_AP)
+ if (carray[i]->type != CONN_TYPE_AP)
continue;
+ conn = TO_EDGE_CONN(carray[i]);
/* if it's an internal bridge connection, don't yell its status. */
- severity = (!conn->addr && !conn->port) ? LOG_INFO : LOG_NOTICE;
- if (conn->state == AP_CONN_STATE_CONTROLLER_WAIT) {
- if (now - conn->timestamp_lastread >= options->SocksTimeout) {
+ severity = (!conn->_base.addr && !conn->_base.port)
+ ? LOG_INFO : LOG_NOTICE;
+ if (conn->_base.state == AP_CONN_STATE_CONTROLLER_WAIT) {
+ if (now - conn->_base.timestamp_lastread >= options->SocksTimeout) {
log_fn(severity, LD_APP, "Closing unattached stream.");
connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
}
continue;
}
- else if (conn->state != AP_CONN_STATE_RESOLVE_WAIT &&
- conn->state != AP_CONN_STATE_CONNECT_WAIT)
+ if (conn->_base.state != AP_CONN_STATE_RESOLVE_WAIT &&
+ conn->_base.state != AP_CONN_STATE_CONNECT_WAIT)
continue;
- if (now - conn->timestamp_lastread < 15)
+ cutoff = compute_socks_timeout(conn);
+ if (now - conn->_base.timestamp_lastread < cutoff)
continue;
circ = circuit_get_by_edge_conn(conn);
if (!circ) { /* it's vanished? */
@@ -357,11 +385,11 @@ connection_ap_expire_beginning(void)
continue;
}
if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
- if (now - conn->timestamp_lastread > options->SocksTimeout) {
+ if (now - conn->_base.timestamp_lastread > options->SocksTimeout) {
log_fn(severity, LD_REND,
"Rend stream is %d seconds late. Giving up on address"
" '%s.onion'.",
- (int)(now - conn->timestamp_lastread),
+ (int)(now - conn->_base.timestamp_lastread),
safe_str(conn->socks_request->address));
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT,
conn->cpath_layer);
@@ -370,26 +398,29 @@ connection_ap_expire_beginning(void)
continue;
}
tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL);
- nickname = build_state_get_exit_nickname(circ->build_state);
- log_fn(severity, LD_APP,
+ nickname = build_state_get_exit_nickname(
+ TO_ORIGIN_CIRCUIT(circ)->build_state);
+ log_fn(cutoff < 15 ? LOG_INFO : severity, LD_APP,
"We tried for %d seconds to connect to '%s' using exit '%s'."
" Retrying on a new circuit.",
- (int)(now - conn->timestamp_lastread),
+ (int)(now - conn->_base.timestamp_lastread),
safe_str(conn->socks_request->address),
nickname ? nickname : "*unnamed*");
/* send an end down the circuit */
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT, conn->cpath_layer);
/* un-mark it as ending, since we're going to reuse it */
- conn->has_sent_end = 0;
+ conn->_base.edge_has_sent_end = 0;
/* kludge to make us not try this circuit again, yet to allow
* current streams on it to survive if they can: make it
* unattractive to use for new streams */
tor_assert(circ->timestamp_dirty);
circ->timestamp_dirty -= options->MaxCircuitDirtiness;
- /* give our stream another 15 seconds to try */
- conn->timestamp_lastread += 15;
+ /* give our stream another 'cutoff' seconds to try */
+ conn->_base.timestamp_lastread += cutoff;
+ if (conn->num_socks_retries < 250) /* avoid overflow */
+ conn->num_socks_retries++;
/* move it back into 'pending' state, and try to attach. */
- if (connection_ap_detach_retriable(conn, circ)<0) {
+ if (connection_ap_detach_retriable(conn, TO_ORIGIN_CIRCUIT(circ))<0) {
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
}
} /* end for */
@@ -403,6 +434,7 @@ connection_ap_attach_pending(void)
{
connection_t **carray;
connection_t *conn;
+ edge_connection_t *edge_conn;
int n, i;
get_connection_array(&carray, &n);
@@ -413,8 +445,9 @@ connection_ap_attach_pending(void)
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
+ edge_conn = TO_EDGE_CONN(conn);
+ if (connection_ap_handshake_attach_circuit(edge_conn) < 0) {
+ connection_mark_unattached_ap(edge_conn, END_STREAM_REASON_CANT_ATTACH);
}
}
}
@@ -427,17 +460,17 @@ connection_ap_attach_pending(void)
* Returns -1 on err, 1 on success, 0 on not-yet-sure.
*/
int
-connection_ap_detach_retriable(connection_t *conn, circuit_t *circ)
+connection_ap_detach_retriable(edge_connection_t *conn, origin_circuit_t *circ)
{
control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE);
- conn->timestamp_lastread = time(NULL);
+ conn->_base.timestamp_lastread = time(NULL);
if (! get_options()->LeaveStreamsUnattached) {
- conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- circuit_detach_stream(circ,conn);
+ conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ circuit_detach_stream(TO_CIRCUIT(circ),conn);
return connection_ap_handshake_attach_circuit(conn);
} else {
- conn->state = AP_CONN_STATE_CONTROLLER_WAIT;
- circuit_detach_stream(circ,conn);
+ conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
+ circuit_detach_stream(TO_CIRCUIT(circ),conn);
return 0;
}
}
@@ -612,9 +645,9 @@ addressmap_rewrite(char *address, size_t maxlen)
/** Return 1 if <b>address</b> is already registered, else return 0 */
int
-addressmap_already_mapped(const char *address)
+addressmap_have_mapping(const char *address)
{
- return strmap_get(addressmap, address) ? 1 : 0;
+ return strmap_get_lc(addressmap, address) ? 1 : 0;
}
/** Register a request to map <b>address</b> to <b>new_address</b>,
@@ -682,7 +715,7 @@ client_dns_incr_failures(const char *address)
addressmap_entry_t *ent = strmap_get(addressmap, address);
if (!ent) {
ent = tor_malloc_zero(sizeof(addressmap_entry_t));
- ent->expires = time(NULL)+MAX_DNS_ENTRY_AGE;
+ ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE;
strmap_set(addressmap,address,ent);
}
++ent->num_resolve_failures;
@@ -712,7 +745,7 @@ client_dns_clear_failures(const char *address)
* ".exitname.exit" before registering the mapping.
*
* If <b>ttl</b> is nonnegative, the mapping will be valid for
- * <b>ttl</b>seconds.
+ * <b>ttl</b>seconds; otherwise, we use the default.
*/
void
client_dns_set_addressmap(const char *address, uint32_t val,
@@ -726,10 +759,13 @@ client_dns_set_addressmap(const char *address, uint32_t val,
char extendedval[INET_NTOA_BUF_LEN+MAX_HEX_NICKNAME_LEN+10];
char valbuf[INET_NTOA_BUF_LEN];
- tor_assert(address); tor_assert(val);
+ tor_assert(address);
+ tor_assert(val);
- if (ttl<0 || ttl>MAX_DNS_ENTRY_AGE)
- ttl = MAX_DNS_ENTRY_AGE;
+ if (ttl<0)
+ ttl = DEFAULT_DNS_TTL;
+ else
+ ttl = dns_clip_ttl(ttl);
if (tor_inet_aton(address, &in))
return; /* If address was an IP address already, don't add a mapping. */
@@ -1005,8 +1041,8 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
* rendezvous descriptor is already here and fresh enough).
*/
int
-connection_ap_handshake_rewrite_and_attach(connection_t *conn,
- circuit_t *circ)
+connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
+ origin_circuit_t *circ)
{
socks_request_t *socks = conn->socks_request;
hostname_type_t addresstype;
@@ -1105,7 +1141,7 @@ connection_ap_handshake_rewrite_and_attach(connection_t *conn,
return 0;
}
rep_hist_note_used_resolve(time(NULL)); /* help predict this next time */
- } else { /* socks->command == SOCKS_COMMAND_CONNECT */
+ } else if (socks->command == SOCKS_COMMAND_CONNECT) {
if (socks->port == 0) {
log_notice(LD_APP,"Application asked to connect to port 0. Refusing.");
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
@@ -1124,13 +1160,20 @@ connection_ap_handshake_rewrite_and_attach(connection_t *conn,
routers with this nickname */
conn->chosen_exit_name =
tor_strdup(hex_str(r->cache_info.identity_digest, DIGEST_LEN));
+ conn->_base.chosen_exit_optional = 1;
}
}
/* help predict this next time */
rep_hist_note_used_port(socks->port, time(NULL));
+ } else if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
+ // XXXX NM Do anything here?
+
+ rep_hist_note_used_resolve(time(NULL)); /* help predict this next time */
+ } else {
+ tor_fragile_assert();
}
- conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
if ((circ &&
connection_ap_handshake_attach_chosen_circuit(conn, circ) < 0) ||
(!circ &&
@@ -1144,7 +1187,7 @@ connection_ap_handshake_rewrite_and_attach(connection_t *conn,
rend_cache_entry_t *entry;
int r;
- if (socks->command == SOCKS_COMMAND_RESOLVE) {
+ if (socks->command != SOCKS_COMMAND_CONNECT) {
/* if it's a resolve request, fail it right now, rather than
* building all the circuits and then realizing it won't work. */
log_warn(LD_APP,
@@ -1175,21 +1218,21 @@ connection_ap_handshake_rewrite_and_attach(connection_t *conn,
return -1;
}
if (r==0) {
- conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str(conn->rend_query));
rend_client_refetch_renddesc(conn->rend_query);
} else { /* r > 0 */
#define NUM_SECONDS_BEFORE_REFETCH (60*15)
if (time(NULL) - entry->received < NUM_SECONDS_BEFORE_REFETCH) {
- conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here and fresh enough. Great.");
if (connection_ap_handshake_attach_circuit(conn) < 0) {
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return -1;
}
} else {
- conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Stale descriptor %s. Refetching.",
safe_str(conn->rend_query));
rend_client_refetch_renddesc(conn->rend_query);
@@ -1200,6 +1243,108 @@ connection_ap_handshake_rewrite_and_attach(connection_t *conn,
return 0; /* unreached but keeps the compiler happy */
}
+#ifdef TRANS_PF
+static int pf_socket = -1;
+static int
+get_pf_socket(void)
+{
+ int pf;
+ /* Ideally, this should be opened before dropping privs. */
+ if (pf_socket >= 0)
+ return pf_socket;
+
+#ifdef OPENBSD
+ /* only works on OpenBSD */
+ pf = open("/dev/pf", O_RDONLY);
+#else
+ /* works on NetBSD and FreeBSD */
+ pf = open("/dev/pf", O_RDWR);
+#endif
+
+ if (pf < 0) {
+ log_warn(LD_NET, "open(\"/dev/pf\") failed: %s", strerror(errno));
+ return -1;
+ }
+
+ pf_socket = pf;
+}
+#endif
+
+/** Fetch the original destination address and port from a
+ * system-specific interface and put them into a
+ * socks_request_t as if they came from a socks request.
+ *
+ * Return -1 if an error prevents fetching the destination,
+ * else return 0.
+ */
+static int
+connection_ap_get_original_destination(edge_connection_t *conn,
+ socks_request_t *req)
+{
+#ifdef TRANS_NETFILTER
+ /* Linux 2.4+ */
+ struct sockaddr_in orig_dst;
+ socklen_t orig_dst_len = sizeof(orig_dst);
+ char tmpbuf[INET_NTOA_BUF_LEN];
+
+ if (getsockopt(conn->_base.s, SOL_IP, SO_ORIGINAL_DST,
+ (struct sockaddr*)&orig_dst, &orig_dst_len) < 0) {
+ int e = tor_socket_errno(conn->_base.s);
+ log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e));
+ return -1;
+ }
+
+ tor_inet_ntoa(&orig_dst.sin_addr, tmpbuf, sizeof(tmpbuf));
+ strlcpy(req->address, tmpbuf, sizeof(req->address));
+ req->port = ntohs(orig_dst.sin_port);
+
+ return 0;
+#elif defined(TRANS_PF)
+ struct sockaddr_in proxy_addr;
+ socklen_t proxy_addr_len = sizeof(proxy_addr);
+ char tmpbuf[INET_NTOA_BUF_LEN];
+ struct pfioc_natlook pnl;
+ int pf = -1;
+
+ if (getsockname(conn->_base.s, (struct sockaddr*)&proxy_addr,
+ &proxy_addr_len) < 0) {
+ int e = tor_socket_errno(conn->_base.s);
+ log_warn(LD_NET, "getsockname() failed: %s", tor_socket_strerror(e));
+ return -1;
+ }
+
+ memset(&pnl, 0, sizeof(pnl));
+ pnl.af = AF_INET;
+ pnl.proto = IPPROTO_TCP;
+ pnl.direction = PF_OUT;
+ pnl.saddr.v4.s_addr = htonl(conn->_base.addr);
+ pnl.sport = htons(conn->_base.port);
+ pnl.daddr.v4.s_addr = proxy_addr.sin_addr.s_addr;
+ pnl.dport = proxy_addr.sin_port;
+
+ pf = get_pf_socket();
+ if (pf<0)
+ return -1;
+
+ if (ioctl(pf, DIOCNATLOOK, &pnl) < 0) {
+ log_warn(LD_NET, "ioctl(DIOCNATLOOK) failed: %s", strerror(errno));
+ return -1;
+ }
+
+ tor_inet_ntoa(&pnl.rdaddr.v4, tmpbuf, sizeof(tmpbuf));
+ strlcpy(req->address, tmpbuf, sizeof(req->address));
+ req->port = ntohs(pnl.rdport);
+
+ return 0;
+#else
+ (void)conn;
+ (void)req;
+ log_warn(LD_BUG, "Called connection_ap_get_original_destination, but no "
+ "transparent proxy method was configured.");
+ return -1;
+#endif
+}
+
/** connection_edge_process_inbuf() found a conn in state
* socks_wait. See if conn->inbuf has the right bytes to proceed with
* the socks handshake.
@@ -1211,25 +1356,25 @@ connection_ap_handshake_rewrite_and_attach(connection_t *conn,
* for close), else return 0.
*/
static int
-connection_ap_handshake_process_socks(connection_t *conn)
+connection_ap_handshake_process_socks(edge_connection_t *conn)
{
socks_request_t *socks;
int sockshere;
or_options_t *options = get_options();
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_AP);
- tor_assert(conn->state == AP_CONN_STATE_SOCKS_WAIT);
+ tor_assert(conn->_base.type == CONN_TYPE_AP);
+ tor_assert(conn->_base.state == AP_CONN_STATE_SOCKS_WAIT);
tor_assert(conn->socks_request);
socks = conn->socks_request;
log_debug(LD_APP,"entered.");
- sockshere = fetch_from_buf_socks(conn->inbuf, socks,
+ sockshere = fetch_from_buf_socks(conn->_base.inbuf, socks,
options->TestSocks, options->SafeSocks);
if (sockshere == 0) {
if (socks->replylen) {
- connection_write_to_buf(socks->reply, socks->replylen, conn);
+ connection_write_to_buf(socks->reply, socks->replylen, TO_CONN(conn));
/* zero it out so we can do another round of negotiation */
socks->replylen = 0;
} else {
@@ -1256,7 +1401,49 @@ connection_ap_handshake_process_socks(connection_t *conn)
control_event_stream_status(conn, STREAM_EVENT_NEW_RESOLVE);
if (options->LeaveStreamsUnattached) {
- conn->state = AP_CONN_STATE_CONTROLLER_WAIT;
+ conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
+ return 0;
+ }
+ return connection_ap_handshake_rewrite_and_attach(conn, NULL);
+}
+
+/** connection_edge_process_inbuf() found a conn in state
+ * origdst_wait. Get the original destination and
+ * send it to connection_ap_handshake_rewrite_and_attach().
+ *
+ * Return -1 if an unexpected error with conn (and it should be marked
+ * for close), else return 0.
+ */
+static int
+connection_ap_process_transparent(edge_connection_t *conn)
+{
+ socks_request_t *socks;
+ or_options_t *options = get_options();
+
+ tor_assert(conn);
+ tor_assert(conn->_base.type == CONN_TYPE_AP);
+ tor_assert(conn->_base.state == AP_CONN_STATE_ORIGDST_WAIT);
+ tor_assert(conn->socks_request);
+ socks = conn->socks_request;
+
+ /* pretend that a socks handshake completed so we don't try to
+ * send a socks reply down a transparent conn */
+ socks->command = SOCKS_COMMAND_CONNECT;
+ socks->has_finished = 1;
+
+ log_debug(LD_APP,"entered.");
+
+ if (connection_ap_get_original_destination(conn, socks) < 0) {
+ log_warn(LD_APP,"Fetching original destination failed. Closing.");
+ connection_mark_unattached_ap(conn, 0);
+ return -1;
+ }
+ /* we have the original destination */
+
+ control_event_stream_status(conn, STREAM_EVENT_NEW);
+
+ if (options->LeaveStreamsUnattached) {
+ conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
return 0;
}
return connection_ap_handshake_rewrite_and_attach(conn, NULL);
@@ -1266,9 +1453,9 @@ connection_ap_handshake_process_socks(connection_t *conn)
* already in use; return it. Return 0 if can't get a unique stream_id.
*/
static uint16_t
-get_unique_stream_id_by_circ(circuit_t *circ)
+get_unique_stream_id_by_circ(origin_circuit_t *circ)
{
- connection_t *tmpconn;
+ edge_connection_t *tmpconn;
uint16_t test_stream_id;
uint32_t attempts=0;
@@ -1293,24 +1480,25 @@ again:
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
int
-connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ)
+connection_ap_handshake_send_begin(edge_connection_t *ap_conn,
+ origin_circuit_t *circ)
{
char payload[CELL_PAYLOAD_SIZE];
int payload_len;
- tor_assert(ap_conn->type == CONN_TYPE_AP);
- tor_assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
+ tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
if (ap_conn->stream_id==0) {
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
- circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
return -1;
}
tor_snprintf(payload,RELAY_PAYLOAD_SIZE, "%s:%d",
- (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) ?
+ (circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL) ?
ap_conn->socks_request->address : "",
ap_conn->socks_request->port);
payload_len = strlen(payload)+1;
@@ -1318,16 +1506,17 @@ connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ)
log_debug(LD_APP,
"Sending relay cell to begin stream %d.", ap_conn->stream_id);
- if (connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_BEGIN,
+ if (connection_edge_send_command(ap_conn, TO_CIRCUIT(circ),
+ RELAY_COMMAND_BEGIN,
payload, payload_len,
ap_conn->cpath_layer) < 0)
return -1; /* circuit is closed, don't continue */
ap_conn->package_window = STREAMWINDOW_START;
ap_conn->deliver_window = STREAMWINDOW_START;
- ap_conn->state = AP_CONN_STATE_CONNECT_WAIT;
+ ap_conn->_base.state = AP_CONN_STATE_CONNECT_WAIT;
log_info(LD_APP,"Address/port sent, ap socket %d, n_circ_id %d",
- ap_conn->s, circ->n_circ_id);
+ ap_conn->_base.s, circ->_base.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT);
return 0;
}
@@ -1338,38 +1527,62 @@ connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ)
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
int
-connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ)
+connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
+ origin_circuit_t *circ)
{
- int payload_len;
+ int payload_len, command;
const char *string_addr;
+ char inaddr_buf[32];
- tor_assert(ap_conn->type == CONN_TYPE_AP);
- tor_assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
+ command = ap_conn->socks_request->command;
+
+ tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
+ tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
- tor_assert(ap_conn->socks_request->command == SOCKS_COMMAND_RESOLVE);
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL);
+ tor_assert(command == SOCKS_COMMAND_RESOLVE ||
+ command == SOCKS_COMMAND_RESOLVE_PTR);
+ tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL);
ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
if (ap_conn->stream_id==0) {
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
- circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
return -1;
}
- string_addr = ap_conn->socks_request->address;
- payload_len = strlen(string_addr)+1;
- tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
+ if (command == SOCKS_COMMAND_RESOLVE) {
+ string_addr = ap_conn->socks_request->address;
+ payload_len = strlen(string_addr)+1;
+ tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
+ } else {
+ struct in_addr in;
+ uint32_t a;
+ if (tor_inet_aton(ap_conn->socks_request->address, &in) == 0) {
+ connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
+ return -1;
+ }
+ a = ntohl(in.s_addr);
+ tor_snprintf(inaddr_buf, sizeof(inaddr_buf), "%d.%d.%d.%d.in-addr.arpa",
+ (int)(uint8_t)((a )&0xff),
+ (int)(uint8_t)((a>>8 )&0xff),
+ (int)(uint8_t)((a>>16)&0xff),
+ (int)(uint8_t)((a>>24)&0xff));
+ string_addr = inaddr_buf;
+ payload_len = strlen(inaddr_buf)+1;
+ tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
+ }
log_debug(LD_APP,
"Sending relay cell to begin stream %d.", ap_conn->stream_id);
- if (connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_RESOLVE,
+ if (connection_edge_send_command(ap_conn, TO_CIRCUIT(circ),
+ RELAY_COMMAND_RESOLVE,
string_addr, payload_len, ap_conn->cpath_layer) < 0)
return -1; /* circuit is closed, don't continue */
- ap_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
+ ap_conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
log_info(LD_APP,"Address sent for resolve, ap socket %d, n_circ_id %d",
- ap_conn->s, circ->n_circ_id);
+ ap_conn->_base.s, circ->_base.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_RESOLVE);
return 0;
}
@@ -1384,7 +1597,7 @@ int
connection_ap_make_bridge(char *address, uint16_t port)
{
int fd[2];
- connection_t *conn;
+ edge_connection_t *conn;
int err;
log_info(LD_APP,"Making AP bridge to %s:%d ...",safe_str(address),port);
@@ -1396,11 +1609,14 @@ connection_ap_make_bridge(char *address, uint16_t port)
return -1;
}
+ tor_assert(fd[0] >= 0);
+ tor_assert(fd[1] >= 0);
+
set_socket_nonblocking(fd[0]);
set_socket_nonblocking(fd[1]);
- conn = connection_new(CONN_TYPE_AP);
- conn->s = fd[0];
+ conn = TO_EDGE_CONN(connection_new(CONN_TYPE_AP));
+ conn->_base.s = fd[0];
/* populate conn->socks_request */
@@ -1412,18 +1628,18 @@ connection_ap_make_bridge(char *address, uint16_t port)
conn->socks_request->port = port;
conn->socks_request->command = SOCKS_COMMAND_CONNECT;
- conn->address = tor_strdup("(local bridge)");
- conn->addr = 0;
- conn->port = 0;
+ conn->_base.address = tor_strdup("(local bridge)");
+ conn->_base.addr = 0;
+ conn->_base.port = 0;
- if (connection_add(conn) < 0) { /* no space, forget it */
- connection_free(conn); /* this closes fd[0] */
+ if (connection_add(TO_CONN(conn)) < 0) { /* no space, forget it */
+ connection_free(TO_CONN(conn)); /* this closes fd[0] */
tor_close_socket(fd[1]);
return -1;
}
- conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- connection_start_reading(conn);
+ conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ connection_start_reading(TO_CONN(conn));
/* attaching to a dirty circuit is fine */
if (connection_ap_handshake_attach_circuit(conn) < 0) {
@@ -1437,18 +1653,18 @@ connection_ap_make_bridge(char *address, uint16_t port)
}
/** Send an answer to an AP connection that has requested a DNS lookup
- * via SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6) or
+ * via SOCKS. The type should be one of RESOLVED_TYPE_(IPV4|IPV6|HOSTNAME) or
* -1 for unreachable; the answer should be in the format specified
* in the socks extensions document.
**/
void
-connection_ap_handshake_socks_resolved(connection_t *conn,
+connection_ap_handshake_socks_resolved(edge_connection_t *conn,
int answer_type,
size_t answer_len,
const char *answer,
int ttl)
{
- char buf[256];
+ char buf[384];
size_t replylen;
if (answer_type == RESOLVED_TYPE_IPV4) {
@@ -1487,6 +1703,14 @@ connection_ap_handshake_socks_resolved(connection_t *conn,
memcpy(buf+4, answer, 16); /* address */
set_uint16(buf+20, 0); /* port == 0. */
replylen = 22;
+ } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
+ buf[1] = SOCKS5_SUCCEEDED;
+ buf[2] = 0; /* reserved */
+ buf[3] = 0x03; /* Domainname address type */
+ buf[4] = (char)answer_len;
+ memcpy(buf+5, answer, answer_len); /* address */
+ set_uint16(buf+5+answer_len, 0); /* port == 0. */
+ replylen = 5+answer_len+2;
} else {
buf[1] = SOCKS5_HOST_UNREACHABLE;
memset(buf+2, 0, 8);
@@ -1509,9 +1733,10 @@ connection_ap_handshake_socks_resolved(connection_t *conn,
* If <b>reply</b> is undefined, <b>status</b> can't be 0.
*/
void
-connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
+connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
size_t replylen,
- socks5_reply_status_t status) {
+ socks5_reply_status_t status)
+{
char buf[256];
tor_assert(conn->socks_request); /* make sure it's an AP stream */
@@ -1524,7 +1749,7 @@ connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
return;
}
if (replylen) { /* we already have a reply in mind */
- connection_write_to_buf(reply, replylen, conn);
+ connection_write_to_buf(reply, replylen, TO_CONN(conn));
conn->socks_request->has_finished = 1;
return;
}
@@ -1534,7 +1759,7 @@ connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
#define SOCKS4_REJECT 91
buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT);
/* leave version, destport, destip zero */
- connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, conn);
+ connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, TO_CONN(conn));
} else if (conn->socks_request->socks_version == 5) {
buf[0] = 5; /* version 5 */
buf[1] = (char)status;
@@ -1542,7 +1767,7 @@ connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
buf[3] = 1; /* ipv4 addr */
memset(buf+4,0,6); /* Set external addr/port to 0.
The spec doesn't seem to say what to do here. -RD */
- connection_write_to_buf(buf,10,conn);
+ connection_write_to_buf(buf,10,TO_CONN(conn));
}
/* If socks_version isn't 4 or 5, don't send anything.
* This can happen in the case of AP bridges. */
@@ -1569,7 +1794,7 @@ connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
int
connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
{
- connection_t *n_stream;
+ edge_connection_t *n_stream;
relay_header_t rh;
char *address=NULL;
uint16_t port;
@@ -1590,16 +1815,19 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
relay_header_unpack(&rh, cell->payload);
if (!memchr(cell->payload+RELAY_HEADER_SIZE, 0, rh.length)) {
- log_warn(LD_PROTOCOL,"relay begin cell has no \\0. Dropping.");
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Relay begin cell has no \\0. Dropping.");
return 0;
}
- if (parse_addr_port(cell->payload+RELAY_HEADER_SIZE,&address,NULL,&port)<0) {
- log_warn(LD_PROTOCOL,"Unable to parse addr:port in relay begin cell. "
- "Dropping.");
+ if (parse_addr_port(LOG_PROTOCOL_WARN, cell->payload+RELAY_HEADER_SIZE,
+ &address,NULL,&port)<0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Unable to parse addr:port in relay begin cell. Dropping.");
return 0;
}
if (port==0) {
- log_warn(LD_PROTOCOL,"Missing port in relay begin cell. Dropping.");
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Missing port in relay begin cell. Dropping.");
tor_free(address);
return 0;
}
@@ -1614,29 +1842,30 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
#endif
log_debug(LD_EXIT,"Creating new exit connection.");
- n_stream = connection_new(CONN_TYPE_EXIT);
- n_stream->purpose = EXIT_PURPOSE_CONNECT;
+ n_stream = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
+ n_stream->_base.purpose = EXIT_PURPOSE_CONNECT;
n_stream->stream_id = rh.stream_id;
- n_stream->port = port;
+ n_stream->_base.port = port;
/* leave n_stream->s at -1, because it's not yet valid */
n_stream->package_window = STREAMWINDOW_START;
n_stream->deliver_window = STREAMWINDOW_START;
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+ origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
log_debug(LD_REND,"begin is for rendezvous. configuring stream.");
- n_stream->address = tor_strdup("(rendezvous)");
- n_stream->state = EXIT_CONN_STATE_CONNECTING;
- strlcpy(n_stream->rend_query, circ->rend_query,
+ n_stream->_base.address = tor_strdup("(rendezvous)");
+ n_stream->_base.state = EXIT_CONN_STATE_CONNECTING;
+ strlcpy(n_stream->rend_query, origin_circ->rend_query,
sizeof(n_stream->rend_query));
tor_assert(connection_edge_is_rendezvous_stream(n_stream));
assert_circuit_ok(circ);
- if (rend_service_set_connection_addr_port(n_stream, circ) < 0) {
+ if (rend_service_set_connection_addr_port(n_stream, origin_circ) < 0) {
log_info(LD_REND,"Didn't find rendezvous service (port %d)",
- n_stream->port);
+ n_stream->_base.port);
connection_edge_end(n_stream, END_STREAM_REASON_EXITPOLICY,
n_stream->cpath_layer);
- connection_free(n_stream);
+ connection_free(TO_CONN(n_stream));
/* knock the whole thing down, somebody screwed up */
circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED);
tor_free(address);
@@ -1644,12 +1873,12 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
}
assert_circuit_ok(circ);
log_debug(LD_REND,"Finished assigning addr/port");
- n_stream->cpath_layer = circ->cpath->prev; /* link it */
+ n_stream->cpath_layer = origin_circ->cpath->prev; /* link it */
/* add it into the linked list of n_streams on this circuit */
- n_stream->next_stream = circ->n_streams;
+ n_stream->next_stream = origin_circ->p_streams;
n_stream->on_circuit = circ;
- circ->n_streams = n_stream;
+ origin_circ->p_streams = n_stream;
assert_circuit_ok(circ);
connection_exit_connect(n_stream);
@@ -1657,27 +1886,29 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
return 0;
}
tor_strlower(address);
- n_stream->address = address;
- n_stream->state = EXIT_CONN_STATE_RESOLVEFAILED;
+ n_stream->_base.address = address;
+ n_stream->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
/* default to failed, change in dns_resolve if it turns out not to fail */
if (we_are_hibernating()) {
connection_edge_end(n_stream, END_STREAM_REASON_HIBERNATING,
n_stream->cpath_layer);
- connection_free(n_stream);
+ connection_free(TO_CONN(n_stream));
return 0;
}
+ log_debug(LD_EXIT,"about to start the dns_resolve().");
/* send it off to the gethostbyname farm */
switch (dns_resolve(n_stream)) {
case 1: /* resolve worked */
/* add it into the linked list of n_streams on this circuit */
- n_stream->next_stream = circ->n_streams;
+ n_stream->next_stream = TO_OR_CIRCUIT(circ)->n_streams;
n_stream->on_circuit = circ;
- circ->n_streams = n_stream;
+ TO_OR_CIRCUIT(circ)->n_streams = n_stream;
assert_circuit_ok(circ);
+ log_debug(LD_EXIT,"about to call connection_exit_connect().");
connection_exit_connect(n_stream);
return 0;
case -1: /* resolve failed */
@@ -1685,9 +1916,9 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
break;
case 0: /* resolve added to pending list */
/* add it into the linked list of resolving_streams on this circuit */
- n_stream->next_stream = circ->resolving_streams;
+ n_stream->next_stream = TO_OR_CIRCUIT(circ)->resolving_streams;
n_stream->on_circuit = circ;
- circ->resolving_streams = n_stream;
+ TO_OR_CIRCUIT(circ)->resolving_streams = n_stream;
assert_circuit_ok(circ);
;
}
@@ -1699,12 +1930,12 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
* begin resolving the hostname, and (eventually) reply with a RESOLVED cell.
*/
int
-connection_exit_begin_resolve(cell_t *cell, circuit_t *circ)
+connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ)
{
- connection_t *dummy_conn;
+ edge_connection_t *dummy_conn;
relay_header_t rh;
- assert_circuit_ok(circ);
+ assert_circuit_ok(TO_CIRCUIT(circ));
relay_header_unpack(&rh, cell->payload);
/* This 'dummy_conn' only exists to remember the stream ID
@@ -1714,13 +1945,13 @@ connection_exit_begin_resolve(cell_t *cell, circuit_t *circ)
* resolved; but if we didn't store them in a connection like this,
* the housekeeping in dns.c would get way more complicated.)
*/
- dummy_conn = connection_new(CONN_TYPE_EXIT);
+ dummy_conn = TO_EDGE_CONN(connection_new(CONN_TYPE_EXIT));
dummy_conn->stream_id = rh.stream_id;
- dummy_conn->address = tor_strndup(cell->payload+RELAY_HEADER_SIZE,
- rh.length);
- dummy_conn->port = 0;
- dummy_conn->state = EXIT_CONN_STATE_RESOLVEFAILED;
- dummy_conn->purpose = EXIT_PURPOSE_RESOLVE;
+ dummy_conn->_base.address = tor_strndup(cell->payload+RELAY_HEADER_SIZE,
+ rh.length);
+ dummy_conn->_base.port = 0;
+ dummy_conn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
+ dummy_conn->_base.purpose = EXIT_PURPOSE_RESOLVE;
/* send it off to the gethostbyname farm */
switch (dns_resolve(dummy_conn)) {
@@ -1728,14 +1959,14 @@ connection_exit_begin_resolve(cell_t *cell, circuit_t *circ)
/* Connection freed; don't touch it. */
return 0;
case 1: /* The result was cached; a resolved cell was sent. */
- if (!dummy_conn->marked_for_close)
- connection_free(dummy_conn);
+ if (!dummy_conn->_base.marked_for_close)
+ connection_free(TO_CONN(dummy_conn));
return 0;
case 0: /* resolve added to pending list */
dummy_conn->next_stream = circ->resolving_streams;
- dummy_conn->on_circuit = circ;
+ dummy_conn->on_circuit = TO_CIRCUIT(circ);
circ->resolving_streams = dummy_conn;
- assert_circuit_ok(circ);
+ assert_circuit_ok(TO_CIRCUIT(circ));
break;
}
return 0;
@@ -1749,17 +1980,19 @@ connection_exit_begin_resolve(cell_t *cell, circuit_t *circ)
* streams must not reveal what IP they connected to.)
*/
void
-connection_exit_connect(connection_t *conn)
+connection_exit_connect(edge_connection_t *edge_conn)
{
uint32_t addr;
uint16_t port;
+ connection_t *conn = TO_CONN(edge_conn);
- if (!connection_edge_is_rendezvous_stream(conn) &&
- router_compare_to_my_exit_policy(conn)) {
+ if (!connection_edge_is_rendezvous_stream(edge_conn) &&
+ router_compare_to_my_exit_policy(edge_conn)) {
log_info(LD_EXIT,"%s:%d failed exit policy. Closing.",
escaped_safe_str(conn->address), conn->port);
- connection_edge_end(conn, END_STREAM_REASON_EXITPOLICY, conn->cpath_layer);
- circuit_detach_stream(circuit_get_by_edge_conn(conn), conn);
+ connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY,
+ edge_conn->cpath_layer);
+ circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn);
connection_free(conn);
return;
}
@@ -1790,8 +2023,8 @@ connection_exit_connect(connection_t *conn)
log_debug(LD_EXIT,"about to try connecting");
switch (connection_connect(conn, conn->address, addr, port)) {
case -1:
- connection_edge_end_errno(conn, conn->cpath_layer);
- circuit_detach_stream(circuit_get_by_edge_conn(conn), conn);
+ connection_edge_end_errno(edge_conn, edge_conn->cpath_layer);
+ circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn);
connection_free(conn);
return;
case 0:
@@ -1813,20 +2046,23 @@ connection_exit_connect(connection_t *conn)
connection_watch_events(conn, EV_READ);
/* also, deliver a 'connected' cell back through the circuit. */
- if (connection_edge_is_rendezvous_stream(conn)) { /* rendezvous stream */
+ if (connection_edge_is_rendezvous_stream(edge_conn)) {
+ /* rendezvous stream */
/* don't send an address back! */
- connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
+ connection_edge_send_command(edge_conn,
+ circuit_get_by_edge_conn(edge_conn),
RELAY_COMMAND_CONNECTED,
- NULL, 0, conn->cpath_layer);
+ NULL, 0, edge_conn->cpath_layer);
} else { /* normal stream */
/* This must be the original address, not the redirected address. */
char connected_payload[8];
set_uint32(connected_payload, htonl(conn->addr));
set_uint32(connected_payload+4,
- htonl(MAX_DNS_ENTRY_AGE)); /* XXXX fill with a real TTL */
- connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
+ htonl(dns_clip_ttl(edge_conn->address_ttl)));
+ connection_edge_send_command(edge_conn,
+ circuit_get_by_edge_conn(edge_conn),
RELAY_COMMAND_CONNECTED,
- connected_payload, 8, conn->cpath_layer);
+ connected_payload, 8, edge_conn->cpath_layer);
}
}
@@ -1834,7 +2070,7 @@ connection_exit_connect(connection_t *conn)
* it is a general stream.
*/
int
-connection_edge_is_rendezvous_stream(connection_t *conn)
+connection_edge_is_rendezvous_stream(edge_connection_t *conn)
{
tor_assert(conn);
if (*conn->rend_query) /* XXX */
@@ -1848,10 +2084,10 @@ connection_edge_is_rendezvous_stream(connection_t *conn)
* resolved.)
*/
int
-connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit)
+connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_AP);
+ tor_assert(conn->_base.type == CONN_TYPE_AP);
tor_assert(conn->socks_request);
tor_assert(exit);
@@ -1867,13 +2103,13 @@ connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit)
if (conn->chosen_exit_name) {
if (router_get_by_nickname(conn->chosen_exit_name, 1) != exit) {
/* doesn't match */
- log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
- conn->chosen_exit_name, exit->nickname);
+// log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
+// conn->chosen_exit_name, exit->nickname);
return 0;
}
}
- if (conn->socks_request->command != SOCKS_COMMAND_RESOLVE) {
+ if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
struct in_addr in;
uint32_t addr = 0;
addr_policy_result_t r;
@@ -1883,6 +2119,16 @@ connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit)
exit->exit_policy);
if (r == ADDR_POLICY_REJECTED || r == ADDR_POLICY_PROBABLY_REJECTED)
return 0;
+ } else { /* Some kind of a resolve. */
+
+ /* Can't support reverse lookups without eventdns. */
+ if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
+ exit->has_old_dnsworkers)
+ return 0;
+
+ /* Don't send DNS requests to non-exit servers by default. */
+ if (policy_is_reject_star(exit->exit_policy))
+ return 0;
}
return 1;
}
@@ -1917,14 +2163,14 @@ parse_extended_hostname(char *address)
s = strrchr(address,'.');
if (!s) return 0; /* no dot, thus normal */
if (!strcmp(s+1,"exit")) {
- *s = 0; /* null-terminate it */
+ *s = 0; /* nul-terminate it */
return EXIT_HOSTNAME; /* .exit */
}
if (strcmp(s+1,"onion"))
return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
/* so it is .onion */
- *s = 0; /* null-terminate it */
+ *s = 0; /* nul-terminate it */
if (strlcpy(query, address, REND_SERVICE_ID_LEN+1) >=
REND_SERVICE_ID_LEN+1)
goto failed;
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 6d1a151d3c..76c2abbf13 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -19,24 +19,23 @@ const char connection_or_c_id[] =
#define TIGHT_CERT_ALLOW_SKEW (90*60)
-static int connection_tls_finish_handshake(connection_t *conn);
-static int connection_or_process_cells_from_inbuf(connection_t *conn);
+static int connection_tls_finish_handshake(or_connection_t *conn);
+static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
/**************************************************************/
/** Map from identity digest of connected OR or desired OR to a connection_t
* with that identity digest. If there is more than one such connection_t,
- * they form a linked list, with next_with_same_id as the next pointer.*/
+ * they form a linked list, with next_with_same_id as the next pointer. */
static digestmap_t *orconn_identity_map = NULL;
/** If conn is listed in orconn_identity_map, remove it, and clear
* conn->identity_digest. */
void
-connection_or_remove_from_identity_map(connection_t *conn)
+connection_or_remove_from_identity_map(or_connection_t *conn)
{
- connection_t *tmp;
+ or_connection_t *tmp;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_OR);
if (!orconn_identity_map)
return;
tmp = digestmap_get(orconn_identity_map, conn->identity_digest);
@@ -73,8 +72,9 @@ connection_or_clear_identity_map(void)
for (i = 0; i < n; ++i) {
connection_t* conn = carray[i];
if (conn->type == CONN_TYPE_OR) {
- memset(conn->identity_digest, 0, DIGEST_LEN);
- conn->next_with_same_id = NULL;
+ or_connection_t *or_conn = TO_OR_CONN(conn);
+ memset(or_conn->identity_digest, 0, DIGEST_LEN);
+ or_conn->next_with_same_id = NULL;
}
}
@@ -87,11 +87,10 @@ connection_or_clear_identity_map(void)
/** Change conn->identity_digest to digest, and add conn into
* orconn_digest_map. */
static void
-connection_or_set_identity_digest(connection_t *conn, const char *digest)
+connection_or_set_identity_digest(or_connection_t *conn, const char *digest)
{
- connection_t *tmp;
+ or_connection_t *tmp;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_OR);
tor_assert(digest);
if (!orconn_identity_map)
@@ -136,10 +135,10 @@ cell_unpack(cell_t *dest, const char *src)
}
int
-connection_or_reached_eof(connection_t *conn)
+connection_or_reached_eof(or_connection_t *conn)
{
log_info(LD_OR,"OR connection reached EOF. Closing.");
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return 0;
}
@@ -149,13 +148,14 @@ connection_or_reached_eof(connection_t *conn)
* and hope for better luck next time.
*/
static int
-connection_or_read_proxy_response(connection_t *conn)
+connection_or_read_proxy_response(or_connection_t *or_conn)
{
char *headers;
char *reason=NULL;
int status_code;
time_t date_header;
int compression;
+ connection_t *conn = TO_CONN(or_conn);
switch (fetch_from_buf_http(conn->inbuf,
&headers, MAX_HEADERS_SIZE,
@@ -185,7 +185,7 @@ connection_or_read_proxy_response(connection_t *conn)
"HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
conn->address, escaped(reason));
tor_free(reason);
- if (connection_tls_start_handshake(conn, 0) < 0) {
+ if (connection_tls_start_handshake(or_conn, 0) < 0) {
/* TLS handshaking error of some kind. */
connection_mark_for_close(conn);
@@ -209,12 +209,11 @@ connection_or_read_proxy_response(connection_t *conn)
* (else do nothing).
*/
int
-connection_or_process_inbuf(connection_t *conn)
+connection_or_process_inbuf(or_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_OR);
- switch (conn->state) {
+ switch (conn->_base.state) {
case OR_CONN_STATE_PROXY_READING:
return connection_or_read_proxy_response(conn);
case OR_CONN_STATE_OPEN:
@@ -233,24 +232,22 @@ connection_or_process_inbuf(connection_t *conn)
* return 0.
*/
int
-connection_or_finished_flushing(connection_t *conn)
+connection_or_finished_flushing(or_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_OR);
-
- assert_connection_ok(conn,0);
+ assert_connection_ok(TO_CONN(conn),0);
- switch (conn->state) {
+ switch (conn->_base.state) {
case OR_CONN_STATE_PROXY_FLUSHING:
log_debug(LD_OR,"finished sending CONNECT to proxy.");
- conn->state = OR_CONN_STATE_PROXY_READING;
- connection_stop_writing(conn);
+ conn->_base.state = OR_CONN_STATE_PROXY_READING;
+ connection_stop_writing(TO_CONN(conn));
break;
case OR_CONN_STATE_OPEN:
- connection_stop_writing(conn);
+ connection_stop_writing(TO_CONN(conn));
break;
default:
- log_err(LD_BUG,"BUG: called in unexpected state %d.", conn->state);
+ log_err(LD_BUG,"BUG: called in unexpected state %d.", conn->_base.state);
tor_fragile_assert();
return -1;
}
@@ -260,10 +257,11 @@ connection_or_finished_flushing(connection_t *conn)
/** Connected handler for OR connections: begin the TLS handshake.
*/
int
-connection_or_finished_connecting(connection_t *conn)
+connection_or_finished_connecting(or_connection_t *or_conn)
{
- tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_OR);
+ connection_t *conn;
+ tor_assert(or_conn);
+ conn = TO_CONN(or_conn);
tor_assert(conn->state == OR_CONN_STATE_CONNECTING);
log_debug(LD_OR,"OR connect() to router at %s:%u finished.",
@@ -298,7 +296,7 @@ connection_or_finished_connecting(connection_t *conn)
return 0;
}
- if (connection_tls_start_handshake(conn, 0) < 0) {
+ if (connection_tls_start_handshake(or_conn, 0) < 0) {
/* TLS handshaking error of some kind. */
connection_mark_for_close(conn);
return -1;
@@ -306,63 +304,49 @@ connection_or_finished_connecting(connection_t *conn)
return 0;
}
-/** Initialize <b>conn</b> to include all the relevant data from <b>router</b>.
- * This function is called either from connection_or_connect(), if
- * we initiated the connect, or from connection_tls_finish_handshake()
- * if the other side initiated it.
- */
-static void
-connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *router,
- int started_here)
-{
- or_options_t *options = get_options();
-
- if (!started_here) {
- conn->addr = router->addr;
- conn->port = router->or_port;
- }
- conn->receiver_bucket = conn->bandwidth = (int)options->BandwidthBurst;
- conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey);
- connection_or_set_identity_digest(conn, router->cache_info.identity_digest);
- conn->nickname = tor_strdup(router->nickname);
- tor_free(conn->address);
- conn->address = tor_strdup(router->address);
-}
-
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
* by checking to see if this describes a router we know. */
static void
-connection_or_init_conn_from_address(connection_t *conn,
+connection_or_init_conn_from_address(or_connection_t *conn,
uint32_t addr, uint16_t port,
const char *id_digest,
int started_here)
{
- const char *n;
or_options_t *options = get_options();
routerinfo_t *r = router_get_by_digest(id_digest);
- conn->addr = addr;
- conn->port = port;
- if (r) {
- connection_or_init_conn_from_router(conn, r, started_here);
- return;
- }
- /* This next part isn't really right, but it's good enough for now. */
- conn->receiver_bucket = conn->bandwidth = (int)options->BandwidthBurst;
+ conn->bandwidthrate = (int)options->BandwidthRate;
+ conn->receiver_bucket = conn->bandwidthburst = (int)options->BandwidthBurst;
connection_or_set_identity_digest(conn, id_digest);
- /* If we're an authoritative directory server, we may know a
- * nickname for this router. */
- n = dirserv_get_nickname_by_digest(id_digest);
- if (n) {
- conn->nickname = tor_strdup(n);
+ conn->_base.addr = addr;
+ conn->_base.port = port;
+ if (r) {
+ if (!started_here) {
+ /* Override the addr/port, so our log messages will make sense.
+ * This is dangerous, since if we ever try looking up a conn by
+ * its actual addr/port, we won't remember. Careful! */
+ conn->_base.addr = r->addr;
+ conn->_base.port = r->or_port;
+ }
+ conn->nickname = tor_strdup(r->nickname);
+ tor_free(conn->_base.address);
+ conn->_base.address = tor_strdup(r->address);
} else {
- conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
- conn->nickname[0] = '$';
- base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1,
- conn->identity_digest, DIGEST_LEN);
+ const char *n;
+ /* If we're an authoritative directory server, we may know a
+ * nickname for this router. */
+ n = dirserv_get_nickname_by_digest(id_digest);
+ if (n) {
+ conn->nickname = tor_strdup(n);
+ } else {
+ conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
+ conn->nickname[0] = '$';
+ base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1,
+ conn->identity_digest, DIGEST_LEN);
+ }
+ tor_free(conn->_base.address);
+ conn->_base.address = tor_dup_addr(addr);
}
- tor_free(conn->address);
- conn->address = tor_dup_addr(addr);
}
/** Return the best connection of type OR with the
@@ -374,11 +358,11 @@ connection_or_init_conn_from_address(connection_t *conn,
* 4) Then if there are any non-empty conns, ignore empty conns.
* 5) Of the remaining conns, prefer newer conns.
*/
-connection_t *
+or_connection_t *
connection_or_get_by_identity_digest(const char *digest)
{
int newer;
- connection_t *conn, *best=NULL;
+ or_connection_t *conn, *best=NULL;
if (!orconn_identity_map)
return NULL;
@@ -386,26 +370,26 @@ connection_or_get_by_identity_digest(const char *digest)
conn = digestmap_get(orconn_identity_map, digest);
for (; conn; conn = conn->next_with_same_id) {
- tor_assert(conn->magic == CONNECTION_MAGIC);
- tor_assert(conn->type == CONN_TYPE_OR);
+ tor_assert(conn->_base.magic == OR_CONNECTION_MAGIC);
+ tor_assert(conn->_base.type == CONN_TYPE_OR);
tor_assert(!memcmp(conn->identity_digest, digest, DIGEST_LEN));
- if (conn->marked_for_close)
+ if (conn->_base.marked_for_close)
continue;
if (!best) {
best = conn; /* whatever it is, it's better than nothing. */
continue;
}
- if (best->state == OR_CONN_STATE_OPEN &&
- conn->state != OR_CONN_STATE_OPEN)
+ if (best->_base.state == OR_CONN_STATE_OPEN &&
+ conn->_base.state != OR_CONN_STATE_OPEN)
continue; /* avoid non-open conns if we can */
- newer = best->timestamp_created < conn->timestamp_created;
+ newer = best->_base.timestamp_created < conn->_base.timestamp_created;
- if (!best->is_obsolete && conn->is_obsolete)
+ if (!best->_base.or_is_obsolete && conn->_base.or_is_obsolete)
continue; /* We never prefer obsolete over non-obsolete connections. */
if (
/* We prefer non-obsolete connections: */
- (best->is_obsolete && !conn->is_obsolete) ||
+ (best->_base.or_is_obsolete && !conn->_base.or_is_obsolete) ||
/* If both have circuits we prefer the newer: */
(best->n_circuits && conn->n_circuits && newer) ||
/* If neither has circuits we prefer the newer: */
@@ -432,26 +416,24 @@ connection_or_get_by_identity_digest(const char *digest)
*
* Return the launched conn, or NULL if it failed.
*/
-connection_t *
+or_connection_t *
connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
{
- connection_t *conn;
- routerinfo_t *me;
+ or_connection_t *conn;
or_options_t *options = get_options();
tor_assert(id_digest);
- if (server_mode(options) && (me=router_get_my_routerinfo()) &&
- router_digest_is_me(id_digest)) {
+ if (server_mode(options) && router_digest_is_me(id_digest)) {
log_info(LD_PROTOCOL,"Client asked me to connect to myself. Refusing.");
return NULL;
}
- conn = connection_new(CONN_TYPE_OR);
+ conn = TO_OR_CONN(connection_new(CONN_TYPE_OR));
/* set up conn so it's got all the data we need to remember */
connection_or_init_conn_from_address(conn, addr, port, id_digest, 1);
- conn->state = OR_CONN_STATE_CONNECTING;
+ conn->_base.state = OR_CONN_STATE_CONNECTING;
control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED);
if (options->HttpsProxy) {
@@ -460,7 +442,7 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
port = options->HttpsProxyPort;
}
- switch (connection_connect(conn, conn->address, addr, port)) {
+ switch (connection_connect(TO_CONN(conn), conn->_base.address, addr, port)) {
case -1:
/* If the connection failed immediately, and we're using
* an https proxy, our https proxy is down. Don't blame the
@@ -470,10 +452,10 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
router_set_status(conn->identity_digest, 0);
}
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
- connection_free(conn);
+ connection_free(TO_CONN(conn));
return NULL;
case 0:
- connection_watch_events(conn, EV_READ | EV_WRITE);
+ connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE);
/* writable indicates finish, readable indicates broken link,
error indicates broken link on windows */
return conn;
@@ -496,16 +478,16 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest)
* Return -1 if <b>conn</b> is broken, else return 0.
*/
int
-connection_tls_start_handshake(connection_t *conn, int receiving)
+connection_tls_start_handshake(or_connection_t *conn, int receiving)
{
- conn->state = OR_CONN_STATE_HANDSHAKING;
- conn->tls = tor_tls_new(conn->s, receiving, 0);
+ conn->_base.state = OR_CONN_STATE_HANDSHAKING;
+ conn->tls = tor_tls_new(conn->_base.s, receiving);
if (!conn->tls) {
log_warn(LD_BUG,"tor_tls_new failed. Closing.");
return -1;
}
- connection_start_reading(conn);
- log_debug(LD_OR,"starting TLS handshake on fd %d", conn->s);
+ connection_start_reading(TO_CONN(conn));
+ log_debug(LD_OR,"starting TLS handshake on fd %d", conn->_base.s);
if (connection_tls_continue_handshake(conn) < 0) {
return -1;
}
@@ -518,7 +500,7 @@ connection_tls_start_handshake(connection_t *conn, int receiving)
* Return -1 if <b>conn</b> is broken, else return 0.
*/
int
-connection_tls_continue_handshake(connection_t *conn)
+connection_tls_continue_handshake(or_connection_t *conn)
{
check_no_tls_errors();
switch (tor_tls_handshake(conn->tls)) {
@@ -529,7 +511,7 @@ connection_tls_continue_handshake(connection_t *conn)
case TOR_TLS_DONE:
return connection_tls_finish_handshake(conn);
case TOR_TLS_WANTWRITE:
- connection_start_writing(conn);
+ connection_start_writing(TO_CONN(conn));
log_debug(LD_OR,"wanted write");
return 0;
case TOR_TLS_WANTREAD: /* handshaking conns are *always* reading */
@@ -547,14 +529,12 @@ connection_tls_continue_handshake(connection_t *conn)
* one day so we're clearer.
*/
int
-connection_or_nonopen_was_started_here(connection_t *conn)
+connection_or_nonopen_was_started_here(or_connection_t *conn)
{
- tor_assert(conn->type == CONN_TYPE_OR);
-
- if (tor_digest_is_zero(conn->identity_digest))
- return 0;
- else
- return 1;
+ tor_assert(conn->_base.type == CONN_TYPE_OR);
+ if (!conn->tls)
+ return 1; /* it's still in proxy states or something */
+ return !tor_tls_is_server(conn->tls);
}
/** Conn just completed its handshake. Return 0 if all is well, and
@@ -569,13 +549,13 @@ connection_or_nonopen_was_started_here(connection_t *conn)
* buffer is undefined.)
*
* As side effects,
- * 1) Set conn->circ_id_type according to tor-spec.txt
+ * 1) Set conn->circ_id_type according to tor-spec.txt.
* 2) If we're an authdirserver and we initiated the connection: drop all
* descriptors that claim to be on that IP/port but that aren't
* this guy; and note that this guy is reachable.
*/
static int
-connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
+connection_or_check_valid_handshake(or_connection_t *conn, char *digest_rcvd)
{
routerinfo_t *router;
crypto_pk_env_t *identity_rcvd=NULL;
@@ -586,7 +566,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
check_no_tls_errors();
if (! tor_tls_peer_has_cert(conn->tls)) {
log_info(LD_PROTOCOL,"Peer (%s:%d) didn't send a cert! Closing.",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
return -1;
}
check_no_tls_errors();
@@ -594,17 +574,17 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
sizeof(nickname))) {
log_fn(severity,LD_PROTOCOL,"Other side (%s:%d) has a cert without a "
"valid nickname. Closing.",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
return -1;
}
check_no_tls_errors();
log_debug(LD_OR, "Other side (%s:%d) claims to be router '%s'",
- conn->address, conn->port, nickname);
+ conn->_base.address, conn->_base.port, nickname);
if (tor_tls_verify(severity, conn->tls, &identity_rcvd) < 0) {
log_fn(severity,LD_OR,"Other side, which claims to be router '%s' (%s:%d),"
" has a cert but it's invalid. Closing.",
- nickname, conn->address, conn->port);
+ nickname, conn->_base.address, conn->_base.port);
return -1;
}
check_no_tls_errors();
@@ -625,7 +605,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
log_fn(severity, LD_OR,
"Identity key not as expected for router claiming to be "
"'%s' (%s:%d)",
- nickname, conn->address, conn->port);
+ nickname, conn->_base.address, conn->_base.port);
return -1;
}
@@ -641,7 +621,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
log_fn(severity, LD_OR,
"Identity key not as expected for router at %s:%d: wanted %s "
"but got %s",
- conn->address, conn->port, expected, seen);
+ conn->_base.address, conn->_base.port, expected, seen);
entry_guard_set_status(conn->identity_digest, 0);
router_set_status(conn->identity_digest, 0);
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED);
@@ -651,7 +631,7 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
/* We initiated this connection to address:port. Drop all routers
* with the same address:port and a different key or nickname.
*/
- dirserv_orconn_tls_done(conn->address, conn->port,
+ dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
digest_rcvd, nickname, as_advertised);
}
if (!as_advertised)
@@ -667,14 +647,12 @@ connection_or_check_valid_handshake(connection_t *conn, char *digest_rcvd)
* If he initiated the connection, make sure he's not already connected,
* then initialize conn from the information in router.
*
- * If I'm not a server, set bandwidth to the default OP bandwidth.
- *
* If all is successful, call circuit_n_conn_done() to handle events
* that have been pending on the tls handshake completion. Also set the
* directory to be dirty (only matters if I'm an authdirserver).
*/
static int
-connection_tls_finish_handshake(connection_t *conn)
+connection_tls_finish_handshake(or_connection_t *conn)
{
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
@@ -684,17 +662,9 @@ connection_tls_finish_handshake(connection_t *conn)
return -1;
if (!started_here) {
-#if 0
- connection_t *c;
- if ((c=connection_or_get_by_identity_digest(digest_rcvd))) {
- log_debug(LD_OR,
- "Router '%s' is already connected on fd %d. Dropping fd %d.",
- c->nickname, c->s, conn->s);
- return -1;
- }
-#endif
- connection_or_init_conn_from_address(conn,conn->addr,conn->port,
- digest_rcvd, 0);
+ connection_or_init_conn_from_address(conn,conn->_base.addr,
+ conn->_base.port, digest_rcvd, 0);
+
/* Annotate that we received a TLS connection.
* (Todo: only actually consider ourselves reachable if there
* exists a testing circuit using conn.)
@@ -705,16 +675,12 @@ connection_tls_finish_handshake(connection_t *conn)
* The reason this bandaid is here is because there's a bug in
* Tor 0.1.1.x where middle hops don't always send their create
* cell; so some servers rarely find themselves reachable. */
-// if (!is_local_IP(conn->addr))
+// if (!is_local_IP(conn->_base.addr))
// router_orport_found_reachable();
}
- if (!server_mode(get_options())) { /* If I'm an OP... */
- conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP;
- }
-
directory_set_dirty();
- conn->state = OR_CONN_STATE_OPEN;
+ conn->_base.state = OR_CONN_STATE_OPEN;
control_event_or_conn_status(conn, OR_CONN_EVENT_CONNECTED);
if (started_here) {
rep_hist_note_connect_succeeded(conn->identity_digest, time(NULL));
@@ -724,7 +690,7 @@ connection_tls_finish_handshake(connection_t *conn)
}
router_set_status(conn->identity_digest, 1);
}
- connection_watch_events(conn, EV_READ);
+ connection_watch_events(TO_CONN(conn), EV_READ);
circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */
return 0;
}
@@ -736,18 +702,17 @@ connection_tls_finish_handshake(connection_t *conn)
* ready, then try to flush the record now.
*/
void
-connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn)
+connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
{
char networkcell[CELL_NETWORK_SIZE];
char *n = networkcell;
tor_assert(cell);
tor_assert(conn);
- tor_assert(connection_speaks_cells(conn));
cell_pack(n, cell);
- connection_write_to_buf(n, CELL_NETWORK_SIZE, conn);
+ connection_write_to_buf(n, CELL_NETWORK_SIZE, TO_CONN(conn));
#define MIN_TLS_FLUSHLEN 15872
/* openssl tls record size is 16383, this is close. The goal here is to
@@ -755,26 +720,28 @@ connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn)
* during periods of high load we won't read the entire megabyte from
* input before pushing any data out. It also has the feature of not
* growing huge outbufs unless something is slow. */
- if (conn->outbuf_flushlen-CELL_NETWORK_SIZE < MIN_TLS_FLUSHLEN &&
- conn->outbuf_flushlen >= MIN_TLS_FLUSHLEN) {
- int extra = conn->outbuf_flushlen - MIN_TLS_FLUSHLEN;
- conn->outbuf_flushlen = MIN_TLS_FLUSHLEN;
- if (connection_handle_write(conn) < 0) {
- if (!conn->marked_for_close) {
+ if (conn->_base.outbuf_flushlen-CELL_NETWORK_SIZE < MIN_TLS_FLUSHLEN &&
+ conn->_base.outbuf_flushlen >= MIN_TLS_FLUSHLEN) {
+ int extra = conn->_base.outbuf_flushlen - MIN_TLS_FLUSHLEN;
+ conn->_base.outbuf_flushlen = MIN_TLS_FLUSHLEN;
+ if (connection_handle_write(TO_CONN(conn)) < 0) {
+ if (!conn->_base.marked_for_close) {
/* this connection is broken. remove it. */
log_warn(LD_BUG,
"Bug: unhandled error on write for OR conn (fd %d); removing",
- conn->s);
+ conn->_base.s);
tor_fragile_assert();
- conn->has_sent_end = 1; /* don't cry wolf about duplicate close */
+ // XXX This was supposed to be edge-only!
+ // conn->has_sent_end = 1; /* don't cry wolf about duplicate close */
+
/* XXX do we need a close-immediate here, so we don't try to flush? */
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
}
return;
}
if (extra) {
- conn->outbuf_flushlen += extra;
- connection_start_writing(conn);
+ conn->_base.outbuf_flushlen += extra;
+ connection_start_writing(TO_CONN(conn));
}
}
}
@@ -787,7 +754,7 @@ connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn)
* Always return 0.
*/
static int
-connection_or_process_cells_from_inbuf(connection_t *conn)
+connection_or_process_cells_from_inbuf(or_connection_t *conn)
{
char buf[CELL_NETWORK_SIZE];
cell_t cell;
@@ -795,13 +762,13 @@ connection_or_process_cells_from_inbuf(connection_t *conn)
loop:
log_debug(LD_OR,
"%d: starting, inbuf_datalen %d (%d pending in tls object).",
- conn->s,(int)buf_datalen(conn->inbuf),
+ conn->_base.s,(int)buf_datalen(conn->_base.inbuf),
tor_tls_get_pending_bytes(conn->tls));
- if (buf_datalen(conn->inbuf) < CELL_NETWORK_SIZE) /* whole response
- available? */
+ if (buf_datalen(conn->_base.inbuf) < CELL_NETWORK_SIZE) /* whole response
+ available? */
return 0; /* not yet */
- connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, conn);
+ connection_fetch_from_buf(buf, CELL_NETWORK_SIZE, TO_CONN(conn));
/* retrieve cell info from buf (create the host-order struct from the
* network-order string) */
@@ -819,12 +786,11 @@ loop:
* Return 0.
*/
int
-connection_or_send_destroy(uint16_t circ_id, connection_t *conn, int reason)
+connection_or_send_destroy(uint16_t circ_id, or_connection_t *conn, int reason)
{
cell_t cell;
tor_assert(conn);
- tor_assert(connection_speaks_cells(conn));
memset(&cell, 0, sizeof(cell_t));
cell.circ_id = circ_id;
diff --git a/src/or/control.c b/src/or/control.c
index 55b4fc01bb..b67cd1ce7c 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -131,57 +131,64 @@ static int disable_log_messages = 0;
static int authentication_cookie_is_set = 0;
static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
-static void connection_printf_to_buf(connection_t *conn,
+static void connection_printf_to_buf(control_connection_t *conn,
const char *format, ...)
CHECK_PRINTF(2,3);
/*static*/ size_t write_escaped_data(const char *data, size_t len,
int translate_newlines, char **out);
/*static*/ size_t read_escaped_data(const char *data, size_t len,
int translate_newlines, char **out);
-static void send_control0_message(connection_t *conn, uint16_t type,
+static void send_control0_message(control_connection_t *conn, uint16_t type,
uint32_t len, const char *body);
-static void send_control_done(connection_t *conn);
-static void send_control_done2(connection_t *conn, const char *msg,
+static void send_control_done(control_connection_t *conn);
+static void send_control_done2(control_connection_t *conn, const char *msg,
size_t len);
-static void send_control0_error(connection_t *conn, uint16_t error,
+static void send_control0_error(control_connection_t *conn, uint16_t error,
const char *message);
static void send_control0_event(uint16_t event, uint32_t len,
const char *body);
static void send_control1_event(uint16_t event, const char *format, ...)
CHECK_PRINTF(2,3);
-static int handle_control_setconf(connection_t *conn, uint32_t len,
+static int handle_control_setconf(control_connection_t *conn, uint32_t len,
char *body);
-static int handle_control_resetconf(connection_t *conn, uint32_t len,
+static int handle_control_resetconf(control_connection_t *conn, uint32_t len,
char *body);
-static int handle_control_getconf(connection_t *conn, uint32_t len,
+static int handle_control_getconf(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_setevents(connection_t *conn, uint32_t len,
+static int handle_control_setevents(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_authenticate(connection_t *conn, uint32_t len,
+static int handle_control_authenticate(control_connection_t *conn,
+ uint32_t len,
const char *body);
-static int handle_control_saveconf(connection_t *conn, uint32_t len,
+static int handle_control_saveconf(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_signal(connection_t *conn, uint32_t len,
+static int handle_control_signal(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_mapaddress(connection_t *conn, uint32_t len,
+static int handle_control_mapaddress(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_getinfo(connection_t *conn, uint32_t len,
+static int handle_control_getinfo(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_extendcircuit(connection_t *conn, uint32_t len,
+static int handle_control_extendcircuit(control_connection_t *conn,
+ uint32_t len,
const char *body);
-static int handle_control_setpurpose(connection_t *conn, int for_circuits,
+static int handle_control_setpurpose(control_connection_t *conn,
+ int for_circuits,
uint32_t len, const char *body);
-static int handle_control_attachstream(connection_t *conn, uint32_t len,
+static int handle_control_attachstream(control_connection_t *conn,
+ uint32_t len,
const char *body);
-static int handle_control_postdescriptor(connection_t *conn, uint32_t len,
+static int handle_control_postdescriptor(control_connection_t *conn,
+ uint32_t len,
const char *body);
-static int handle_control_redirectstream(connection_t *conn, uint32_t len,
+static int handle_control_redirectstream(control_connection_t *conn,
+ uint32_t len,
const char *body);
-static int handle_control_closestream(connection_t *conn, uint32_t len,
+static int handle_control_closestream(control_connection_t *conn, uint32_t len,
const char *body);
-static int handle_control_closecircuit(connection_t *conn, uint32_t len,
+static int handle_control_closecircuit(control_connection_t *conn,
+ uint32_t len,
const char *body);
-static int write_stream_target_to_buf(connection_t *conn, char *buf,
+static int write_stream_target_to_buf(edge_connection_t *conn, char *buf,
size_t len);
/** Given a possibly invalid message type code <b>cmd</b>, return a
@@ -235,10 +242,11 @@ control_update_global_event_mask(void)
for (i = 0; i < n_conns; ++i) {
if (conns[i]->type == CONN_TYPE_CONTROL &&
STATE_IS_OPEN(conns[i]->state)) {
- if (STATE_IS_V0(conns[i]->state))
- global_event_mask0 |= conns[i]->event_mask;
+ control_connection_t *conn = TO_CONTROL_CONN(conns[i]);
+ if (STATE_IS_V0(conn->_base.state))
+ global_event_mask0 |= conn->event_mask;
else
- global_event_mask1 |= conns[i]->event_mask;
+ global_event_mask1 |= conn->event_mask;
}
}
@@ -281,10 +289,10 @@ control_adjust_event_log_severity(void)
* <b>conn</b>-\>outbuf
*/
static INLINE void
-connection_write_str_to_buf(const char *s, connection_t *conn)
+connection_write_str_to_buf(const char *s, control_connection_t *conn)
{
size_t len = strlen(s);
- connection_write_to_buf(s, len, conn);
+ connection_write_to_buf(s, len, TO_CONN(conn));
}
/** Given a <b>len</b>-character string in <b>data</b>, made of lines
@@ -446,7 +454,7 @@ get_escaped_string(const char *start, size_t in_len_max,
* Currently the length of the message is limited to 1024 (including the
* ending \n\r\0. */
static void
-connection_printf_to_buf(connection_t *conn, const char *format, ...)
+connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
{
#define CONNECTION_PRINTF_TO_BUF_BUFFERSIZE 1024
va_list ap;
@@ -462,41 +470,41 @@ connection_printf_to_buf(connection_t *conn, const char *format, ...)
buf[CONNECTION_PRINTF_TO_BUF_BUFFERSIZE-2] = '\n';
buf[CONNECTION_PRINTF_TO_BUF_BUFFERSIZE-3] = '\r';
}
- connection_write_to_buf(buf, len, conn);
+ connection_write_to_buf(buf, len, TO_CONN(conn));
}
/** Send a message of type <b>type</b> containing <b>len</b> bytes
* from <b>body</b> along the control connection <b>conn</b> */
static void
-send_control0_message(connection_t *conn, uint16_t type, uint32_t len,
- const char *body)
+send_control0_message(control_connection_t *conn, uint16_t type, uint32_t len,
+ const char *body)
{
char buf[10];
tor_assert(conn);
- tor_assert(STATE_IS_V0(conn->state));
+ tor_assert(STATE_IS_V0(conn->_base.state));
tor_assert(len || !body);
tor_assert(type <= _CONTROL0_CMD_MAX_RECOGNIZED);
if (len < 65536) {
set_uint16(buf, htons(len));
set_uint16(buf+2, htons(type));
- connection_write_to_buf(buf, 4, conn);
+ connection_write_to_buf(buf, 4, TO_CONN(conn));
if (len)
- connection_write_to_buf(body, len, conn);
+ connection_write_to_buf(body, len, TO_CONN(conn));
} else {
set_uint16(buf, htons(65535));
set_uint16(buf+2, htons(CONTROL0_CMD_FRAGMENTHEADER));
set_uint16(buf+4, htons(type));
set_uint32(buf+6, htonl(len));
- connection_write_to_buf(buf, 10, conn);
- connection_write_to_buf(body, 65535-6, conn);
+ connection_write_to_buf(buf, 10, TO_CONN(conn));
+ connection_write_to_buf(body, 65535-6, TO_CONN(conn));
len -= (65535-6);
body += (65535-6);
while (len) {
size_t chunklen = (len<65535)?len:65535;
set_uint16(buf, htons((uint16_t)chunklen));
set_uint16(buf+2, htons(CONTROL0_CMD_FRAGMENT));
- connection_write_to_buf(buf, 4, conn);
- connection_write_to_buf(body, chunklen, conn);
+ connection_write_to_buf(buf, 4, TO_CONN(conn));
+ connection_write_to_buf(body, chunklen, TO_CONN(conn));
len -= chunklen;
body += chunklen;
}
@@ -505,9 +513,9 @@ send_control0_message(connection_t *conn, uint16_t type, uint32_t len,
/** Send a "DONE" message down the control connection <b>conn</b> */
static void
-send_control_done(connection_t *conn)
+send_control_done(control_connection_t *conn)
{
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
send_control0_message(conn, CONTROL0_CMD_DONE, 0, NULL);
} else {
connection_write_str_to_buf("250 OK\r\n", conn);
@@ -518,7 +526,7 @@ send_control_done(connection_t *conn)
* as provided in the <b>len</b> bytes at <b>msg</b>.
*/
static void
-send_control_done2(connection_t *conn, const char *msg, size_t len)
+send_control_done2(control_connection_t *conn, const char *msg, size_t len)
{
if (len==0)
len = strlen(msg);
@@ -528,7 +536,8 @@ send_control_done2(connection_t *conn, const char *msg, size_t len)
/** Send an error message with error code <b>error</b> and body
* <b>message</b> down the connection <b>conn</b> */
static void
-send_control0_error(connection_t *conn, uint16_t error, const char *message)
+send_control0_error(control_connection_t *conn, uint16_t error,
+ const char *message)
{
char buf[256];
size_t len;
@@ -561,11 +570,13 @@ send_control0_event(uint16_t event, uint32_t len, const char *body)
for (i = 0; i < n_conns; ++i) {
if (conns[i]->type == CONN_TYPE_CONTROL &&
!conns[i]->marked_for_close &&
- conns[i]->state == CONTROL_CONN_STATE_OPEN_V0 &&
- conns[i]->event_mask & (1<<event)) {
- send_control0_message(conns[i], CONTROL0_CMD_EVENT, buflen, buf);
- if (event == EVENT_ERR_MSG)
- _connection_controller_force_write(conns[i]);
+ conns[i]->state == CONTROL_CONN_STATE_OPEN_V0) {
+ control_connection_t *control_conn = TO_CONTROL_CONN(conns[i]);
+ if (control_conn->event_mask & (1<<event)) {
+ send_control0_message(control_conn, CONTROL0_CMD_EVENT, buflen, buf);
+ if (event == EVENT_ERR_MSG)
+ _connection_controller_force_write(control_conn);
+ }
}
}
@@ -586,11 +597,13 @@ send_control1_event_string(uint16_t event, const char *msg)
for (i = 0; i < n_conns; ++i) {
if (conns[i]->type == CONN_TYPE_CONTROL &&
!conns[i]->marked_for_close &&
- conns[i]->state == CONTROL_CONN_STATE_OPEN_V1 &&
- conns[i]->event_mask & (1<<event)) {
- connection_write_to_buf(msg, strlen(msg), conns[i]);
- if (event == EVENT_ERR_MSG)
- _connection_controller_force_write(conns[i]);
+ conns[i]->state == CONTROL_CONN_STATE_OPEN_V1) {
+ control_connection_t *control_conn = TO_CONTROL_CONN(conns[i]);
+ if (control_conn->event_mask & (1<<event)) {
+ connection_write_to_buf(msg, strlen(msg), TO_CONN(control_conn));
+ if (event == EVENT_ERR_MSG)
+ _connection_controller_force_write(control_conn);
+ }
}
}
}
@@ -626,7 +639,7 @@ send_control1_event(uint16_t event, const char *format, ...)
}
/** Given a text circuit <b>id</b>, return the corresponding circuit. */
-static circuit_t *
+static origin_circuit_t *
get_circ(const char *id)
{
unsigned long n_id;
@@ -638,17 +651,17 @@ get_circ(const char *id)
}
/** Given a text stream <b>id</b>, return the corresponding AP connection. */
-static connection_t *
+static edge_connection_t *
get_stream(const char *id)
{
unsigned long n_id;
int ok;
- connection_t *conn;
+ edge_connection_t *conn;
n_id = tor_parse_ulong(id, 10, 0, ULONG_MAX, &ok, NULL);
if (!ok)
return NULL;
conn = connection_get_by_global_id(n_id);
- if (!conn || conn->type != CONN_TYPE_AP)
+ if (!conn || conn->_base.type != CONN_TYPE_AP)
return NULL;
return conn;
}
@@ -657,14 +670,14 @@ get_stream(const char *id)
* it passes <b>use_defaults</b> on to options_trial_assign().
*/
static int
-control_setconf_helper(connection_t *conn, uint32_t len, char *body,
+control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
int use_defaults, int clear_first)
{
int r;
config_line_t *lines=NULL;
char *start = body;
char *errstring = NULL;
- int v0 = STATE_IS_V0(conn->state);
+ int v0 = STATE_IS_V0(conn->_base.state);
if (!v0) {
char *config = tor_malloc(len+1);
@@ -761,7 +774,7 @@ control_setconf_helper(connection_t *conn, uint32_t len, char *body,
/** Called when we receive a SETCONF message: parse the body and try
* to update our configuration. Reply with a DONE or ERROR message. */
static int
-handle_control_setconf(connection_t *conn, uint32_t len, char *body)
+handle_control_setconf(control_connection_t *conn, uint32_t len, char *body)
{
return control_setconf_helper(conn, len, body, 0, 1);
}
@@ -769,9 +782,9 @@ handle_control_setconf(connection_t *conn, uint32_t len, char *body)
/** Called when we receive a RESETCONF message: parse the body and try
* to update our configuration. Reply with a DONE or ERROR message. */
static int
-handle_control_resetconf(connection_t *conn, uint32_t len, char *body)
+handle_control_resetconf(control_connection_t *conn, uint32_t len, char *body)
{
- int v0 = STATE_IS_V0(conn->state);
+ int v0 = STATE_IS_V0(conn->_base.state);
tor_assert(!v0);
return control_setconf_helper(conn, len, body, 1, 1);
}
@@ -779,7 +792,8 @@ handle_control_resetconf(connection_t *conn, uint32_t len, char *body)
/** Called when we receive a GETCONF message. Parse the request, and
* reply with a CONFVALUE or an ERROR message */
static int
-handle_control_getconf(connection_t *conn, uint32_t body_len, const char *body)
+handle_control_getconf(control_connection_t *conn, uint32_t body_len,
+ const char *body)
{
smartlist_t *questions = NULL;
smartlist_t *answers = NULL;
@@ -787,9 +801,10 @@ handle_control_getconf(connection_t *conn, uint32_t body_len, const char *body)
char *msg = NULL;
size_t msg_len;
or_options_t *options = get_options();
- int v0 = STATE_IS_V0(conn->state);
+ int v0 = STATE_IS_V0(conn->_base.state);
questions = smartlist_create();
+ (void) body_len; /* body is nul-terminated; so we can ignore len. */
if (v0) {
smartlist_split_string(questions, body, "\n",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@@ -857,7 +872,7 @@ handle_control_getconf(connection_t *conn, uint32_t body_len, const char *body)
tor_assert(strlen(tmp)>4);
tmp[3] = ' ';
msg = smartlist_join_strings(answers, "", 0, &msg_len);
- connection_write_to_buf(msg, msg_len, conn);
+ connection_write_to_buf(msg, msg_len, TO_CONN(conn));
} else {
connection_write_str_to_buf("250 OK\r\n", conn);
}
@@ -881,13 +896,14 @@ handle_control_getconf(connection_t *conn, uint32_t body_len, const char *body)
/** Called when we get a SETEVENTS message: update conn->event_mask,
* and reply with DONE or ERROR. */
static int
-handle_control_setevents(connection_t *conn, uint32_t len, const char *body)
+handle_control_setevents(control_connection_t *conn, uint32_t len,
+ const char *body)
{
uint16_t event_code;
uint32_t event_mask = 0;
unsigned int extended = 0;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
if (len % 2) {
send_control0_error(conn, ERR_SYNTAX,
"Odd number of bytes in setevents message");
@@ -949,7 +965,7 @@ handle_control_setevents(connection_t *conn, uint32_t len, const char *body)
smartlist_free(events);
}
conn->event_mask = event_mask;
- conn->control_events_are_extended = extended;
+ conn->_base.control_events_are_extended = extended;
control_update_global_event_mask();
send_control_done(conn);
@@ -986,13 +1002,14 @@ decode_hashed_password(char *buf, const char *hashed)
* OPEN. Reply with DONE or ERROR.
*/
static int
-handle_control_authenticate(connection_t *conn, uint32_t len, const char *body)
+handle_control_authenticate(control_connection_t *conn, uint32_t len,
+ const char *body)
{
int used_quoted_string = 0;
or_options_t *options = get_options();
char *password;
size_t password_len;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
password = (char*)body;
password_len = len;
} else {
@@ -1046,7 +1063,7 @@ handle_control_authenticate(connection_t *conn, uint32_t len, const char *body)
}
err:
- if (STATE_IS_V0(conn->state))
+ if (STATE_IS_V0(conn->_base.state))
send_control0_error(conn,ERR_REJECTED_AUTHENTICATION,
"Authentication failed");
else {
@@ -1060,12 +1077,12 @@ handle_control_authenticate(connection_t *conn, uint32_t len, const char *body)
}
return 0;
ok:
- log_info(LD_CONTROL, "Authenticated control connection (%d)", conn->s);
+ log_info(LD_CONTROL, "Authenticated control connection (%d)", conn->_base.s);
send_control_done(conn);
- if (STATE_IS_V0(conn->state))
- conn->state = CONTROL_CONN_STATE_OPEN_V0;
+ if (STATE_IS_V0(conn->_base.state))
+ conn->_base.state = CONTROL_CONN_STATE_OPEN_V0;
else {
- conn->state = CONTROL_CONN_STATE_OPEN_V1;
+ conn->_base.state = CONTROL_CONN_STATE_OPEN_V1;
tor_free(password);
}
return 0;
@@ -1074,11 +1091,13 @@ handle_control_authenticate(connection_t *conn, uint32_t len, const char *body)
/** Called when we get a SAVECONF command. Try to flush the current options to
* disk, and report success or failure. */
static int
-handle_control_saveconf(connection_t *conn, uint32_t len,
+handle_control_saveconf(control_connection_t *conn, uint32_t len,
const char *body)
{
+ (void) len;
+ (void) body;
if (options_save_current()<0) {
- if (STATE_IS_V0(conn->state))
+ if (STATE_IS_V0(conn->_base.state))
send_control0_error(conn, ERR_INTERNAL,
"Unable to write configuration to disk.");
else
@@ -1094,11 +1113,11 @@ handle_control_saveconf(connection_t *conn, uint32_t len,
* report success or failure. (If the signal results in a shutdown, success
* may not be reported.) */
static int
-handle_control_signal(connection_t *conn, uint32_t len,
+handle_control_signal(control_connection_t *conn, uint32_t len,
const char *body)
{
int sig;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
if (len != 1) {
send_control0_error(conn, ERR_SYNTAX,
"Body of SIGNAL command too long or too short.");
@@ -1134,14 +1153,16 @@ handle_control_signal(connection_t *conn, uint32_t len,
return 0;
}
- if (control_signal_act(sig) < 0) {
- if (STATE_IS_V0(conn->state))
+ if (!control_signal_check(sig)) {
+ if (STATE_IS_V0(conn->_base.state))
send_control0_error(conn, ERR_SYNTAX, "Unrecognized signal number.");
else
- connection_write_str_to_buf("551 Internal error acting on signal\r\n",
+ connection_write_str_to_buf("551 Unable to act on signal\r\n",
conn);
} else {
+ /* Send DONE first, in case the signal makes us shut down. */
send_control_done(conn);
+ control_signal_act(sig);
}
return 0;
}
@@ -1149,14 +1170,17 @@ handle_control_signal(connection_t *conn, uint32_t len,
/** Called when we get a MAPADDRESS command; try to bind all listed addresses,
* and report success or failrue. */
static int
-handle_control_mapaddress(connection_t *conn, uint32_t len, const char *body)
+handle_control_mapaddress(control_connection_t *conn, uint32_t len,
+ const char *body)
{
smartlist_t *elts;
smartlist_t *lines;
smartlist_t *reply;
char *r;
size_t sz;
- int v0 = STATE_IS_V0(conn->state);
+ int v0 = STATE_IS_V0(conn->_base.state);
+ (void) len; /* body is nul-terminated, so it's safe to ignore the length. */
+
lines = smartlist_create();
elts = smartlist_create();
reply = smartlist_create();
@@ -1252,12 +1276,12 @@ handle_control_mapaddress(connection_t *conn, uint32_t len, const char *body)
if (smartlist_len(reply)) {
((char*)smartlist_get(reply,smartlist_len(reply)-1))[3] = ' ';
r = smartlist_join_strings(reply, "\r\n", 1, &sz);
- connection_write_to_buf(r, sz, conn);
+ connection_write_to_buf(r, sz, TO_CONN(conn));
tor_free(r);
} else {
const char *response =
"512 syntax error: not enough arguments to mapaddress.\r\n";
- connection_write_to_buf(response, strlen(response), conn);
+ connection_write_to_buf(response, strlen(response), TO_CONN(conn));
}
}
@@ -1283,13 +1307,16 @@ list_getinfo_options(void)
"addr-mappings/cache Addresses remapped by DNS cache.\n"
"addr-mappings/configl Addresses remapped from configuration options.\n"
"addr-mappings/control Addresses remapped by a controller.\n"
+ "address The best guess at our external IP address.\n"
"circuit-status Status of each current circuit.\n"
+ "config-file Current location of the \"torrc\" file.\n"
"config/names List of configuration options, types, and documentation.\n"
"desc/id/* Server descriptor by hex ID\n"
"desc/name/* Server descriptor by nickname.\n"
"desc/all-recent Latest server descriptor for every router\n"
"dir/server/* Fetch server descriptors -- see dir-spec.txt\n"
"entry-guards Which nodes will we use as entry guards?\n"
+ "exit-policy/default Default lines appended to config->ExitPolicy\n"
"info/names List of GETINFO options, types, and documentation.\n"
"network-status List of hex IDs, nicknames, server statuses.\n"
"orconn-status Status of each current OR connection.\n"
@@ -1299,7 +1326,7 @@ list_getinfo_options(void)
/** Lookup the 'getinfo' entry <b>question</b>, and return
* the answer in <b>*answer</b> (or NULL if key not recognized).
- * Return 0 if success, or -1 if internal error. */
+ * Return 0 if success, or -1 if recognized but internal error. */
static int
handle_getinfo_helper(const char *question, char **answer)
{
@@ -1323,14 +1350,14 @@ handle_getinfo_helper(const char *question, char **answer)
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
- *answer = tor_strdup(body);
+ *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
}
} else if (!strcmpstart(question, "desc/name/")) {
routerinfo_t *ri = router_get_by_nickname(question+strlen("desc/name/"),1);
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
- *answer = tor_strdup(body);
+ *answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
}
} else if (!strcmp(question, "desc/all-recent")) {
routerlist_t *routerlist = router_get_routerlist();
@@ -1340,7 +1367,8 @@ handle_getinfo_helper(const char *question, char **answer)
{
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
- smartlist_add(sl, tor_strdup(body));
+ smartlist_add(sl,
+ tor_strndup(body, ri->cache_info.signed_descriptor_len));
});
}
*answer = smartlist_join_strings(sl, "", 0, NULL);
@@ -1364,7 +1392,7 @@ handle_getinfo_helper(const char *question, char **answer)
const char *state;
if (! CIRCUIT_IS_ORIGIN(circ) || circ->marked_for_close)
continue;
- path = circuit_list_path(circ,0);
+ path = circuit_list_path(TO_ORIGIN_CIRCUIT(circ),0);
if (circ->state == CIRCUIT_STATE_OPEN)
state = "BUILT";
else if (strlen(path))
@@ -1375,7 +1403,7 @@ handle_getinfo_helper(const char *question, char **answer)
slen = strlen(path)+strlen(state)+20;
s = tor_malloc(slen+1);
tor_snprintf(s, slen, "%lu %s %s",
- (unsigned long)circ->global_identifier,
+ (unsigned long)TO_ORIGIN_CIRCUIT(circ)->global_identifier,
state, path);
smartlist_add(status, s);
tor_free(path);
@@ -1391,19 +1419,24 @@ handle_getinfo_helper(const char *question, char **answer)
get_connection_array(&conns, &n_conns);
for (i=0; i < n_conns; ++i) {
const char *state;
+ edge_connection_t *conn;
char *s;
size_t slen;
circuit_t *circ;
+ origin_circuit_t *origin_circ = NULL;
if (conns[i]->type != CONN_TYPE_AP ||
conns[i]->marked_for_close ||
- conns[i]->state == AP_CONN_STATE_SOCKS_WAIT)
+ conns[i]->state == AP_CONN_STATE_SOCKS_WAIT ||
+ conns[i]->state == AP_CONN_STATE_ORIGDST_WAIT)
continue;
- switch (conns[i]->state)
+ conn = TO_EDGE_CONN(conns[i]);
+ switch (conn->_base.state)
{
case AP_CONN_STATE_CONTROLLER_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
- if (conns[i]->socks_request &&
- conns[i]->socks_request->command == SOCKS_COMMAND_RESOLVE)
+ if (conn->socks_request &&
+ (conn->socks_request->command == SOCKS_COMMAND_RESOLVE ||
+ conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR))
state = "NEWRESOLVE";
else
state = "NEW";
@@ -1417,16 +1450,19 @@ handle_getinfo_helper(const char *question, char **answer)
state = "SUCCEEDED"; break;
default:
log_warn(LD_BUG, "Asked for stream in unknown state %d",
- conns[i]->state);
+ conn->_base.state);
continue;
}
- circ = circuit_get_by_edge_conn(conns[i]);
- write_stream_target_to_buf(conns[i], buf, sizeof(buf));
+ circ = circuit_get_by_edge_conn(conn);
+ if (circ && CIRCUIT_IS_ORIGIN(circ))
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ write_stream_target_to_buf(conn, buf, sizeof(buf));
slen = strlen(buf)+strlen(state)+32;
s = tor_malloc(slen+1);
tor_snprintf(s, slen, "%lu %s %lu %s",
- (unsigned long) conns[i]->global_identifier,state,
- circ?(unsigned long)circ->global_identifier : 0ul,
+ (unsigned long) conn->global_identifier,state,
+ origin_circ?
+ (unsigned long)origin_circ->global_identifier : 0ul,
buf);
smartlist_add(status, s);
}
@@ -1443,10 +1479,11 @@ handle_getinfo_helper(const char *question, char **answer)
char *s;
char name[128];
size_t slen;
- connection_t *conn = conns[i];
- if (conn->type != CONN_TYPE_OR || conn->marked_for_close)
+ or_connection_t *conn;
+ if (conns[i]->type != CONN_TYPE_OR || conns[i]->marked_for_close)
continue;
- if (conn->state == OR_CONN_STATE_OPEN)
+ conn = TO_OR_CONN(conns[i]);
+ if (conn->_base.state == OR_CONN_STATE_OPEN)
state = "CONNECTED";
else if (conn->nickname)
state = "LAUNCHED";
@@ -1456,7 +1493,7 @@ handle_getinfo_helper(const char *question, char **answer)
strlcpy(name, conn->nickname, sizeof(name));
else
tor_snprintf(name, sizeof(name), "%s:%d",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
slen = strlen(name)+strlen(state)+2;
s = tor_malloc(slen+1);
@@ -1482,9 +1519,14 @@ handle_getinfo_helper(const char *question, char **answer)
}
mappings = smartlist_create();
addressmap_get_mappings(mappings, min_e, max_e);
- *answer = smartlist_join_strings(mappings, "\n", 0, NULL);
+ *answer = smartlist_join_strings(mappings, "\r\n", 0, NULL);
SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp));
smartlist_free(mappings);
+ } else if (!strcmp(question, "address")) {
+ uint32_t addr;
+ if (router_pick_published_address(get_options(), &addr) < 0)
+ return -1;
+ *answer = tor_dup_addr(addr);
} else if (!strcmp(question, "dir-usage")) {
*answer = directory_dump_request_log();
} else if (!strcmpstart(question, "dir/server/")) {
@@ -1514,7 +1556,7 @@ handle_getinfo_helper(const char *question, char **answer)
char *cp;
if (!get_options()->DirPort) {
log_warn(LD_CONTROL, "getinfo dir/status/ requires an open dirport.");
- return 0;
+ return -1;
}
status_list = smartlist_create();
dirserv_get_networkstatus_v2(status_list,
@@ -1527,6 +1569,8 @@ handle_getinfo_helper(const char *question, char **answer)
cp += d->dir_len;
});
*cp = '\0';
+ } else if (!strcmpstart(question, "exit-policy/")) {
+ return policies_getinfo_helper(question, answer);
}
return 0;
}
@@ -1534,14 +1578,16 @@ handle_getinfo_helper(const char *question, char **answer)
/** Called when we receive a GETINFO command. Try to fetch all requested
* information, and reply with information or error message. */
static int
-handle_control_getinfo(connection_t *conn, uint32_t len, const char *body)
+handle_control_getinfo(control_connection_t *conn, uint32_t len,
+ const char *body)
{
smartlist_t *questions = NULL;
smartlist_t *answers = NULL;
smartlist_t *unrecognized = NULL;
char *msg = NULL, *ans = NULL;
size_t msg_len;
- int v0 = STATE_IS_V0(conn->state);
+ int v0 = STATE_IS_V0(conn->_base.state);
+ (void) len; /* body is nul-terminated, so it's safe to ignore the length. */
questions = smartlist_create();
if (v0)
@@ -1604,7 +1650,7 @@ handle_control_getinfo(connection_t *conn, uint32_t len, const char *body)
size_t len;
len = write_escaped_data(v, strlen(v), 1, &esc);
connection_printf_to_buf(conn, "250+%s=\r\n", k);
- connection_write_to_buf(esc, len, conn);
+ connection_write_to_buf(esc, len, TO_CONN(conn));
tor_free(esc);
}
}
@@ -1626,20 +1672,23 @@ handle_control_getinfo(connection_t *conn, uint32_t len, const char *body)
return 0;
}
-/** If <b>string</b> contains a recognized purpose (for
+/** If *<b>string</b> contains a recognized purpose (for
* circuits if <b>for_circuits</b> is 1, else for routers),
* possibly prefaced with the string "purpose=", then assign it
- * and return 0. Otherwise return -1. */
+ * and return 0. Otherwise return -1.
+ *
+ * If it's prefaced with "purpose=", then set *<b>string</b> to
+ * the remainder of the string. */
static int
-get_purpose(char *string, int for_circuits, uint8_t *purpose)
+get_purpose(char **string, int for_circuits, uint8_t *purpose)
{
- if (!strcmpstart(string, "purpose="))
- string += strlen("purpose=");
+ if (!strcmpstart(*string, "purpose="))
+ *string += strlen("purpose=");
- if (!strcmp(string, "general"))
+ if (!strcmp(*string, "general"))
*purpose = for_circuits ? CIRCUIT_PURPOSE_C_GENERAL :
ROUTER_PURPOSE_GENERAL;
- else if (!strcmp(string, "controller"))
+ else if (!strcmp(*string, "controller"))
*purpose = for_circuits ? CIRCUIT_PURPOSE_CONTROLLER :
ROUTER_PURPOSE_GENERAL;
else { /* not a recognized purpose */
@@ -1651,17 +1700,17 @@ get_purpose(char *string, int for_circuits, uint8_t *purpose)
/** Called when we get an EXTENDCIRCUIT message. Try to extend the listed
* circuit, and report success or failure. */
static int
-handle_control_extendcircuit(connection_t *conn, uint32_t len,
+handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
const char *body)
{
smartlist_t *router_nicknames=NULL, *routers=NULL;
uint32_t circ_id;
- circuit_t *circ = NULL;
+ origin_circuit_t *circ = NULL;
int zero_circ, v0;
char reply[4];
uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
- v0 = STATE_IS_V0(conn->state);
+ v0 = STATE_IS_V0(conn->_base.state);
router_nicknames = smartlist_create();
if (v0) {
@@ -1704,9 +1753,9 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0);
if (zero_circ && smartlist_len(args)>2) {
- if (get_purpose(smartlist_get(args,2), 1, &intended_purpose) < 0) {
- connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
- (char *)smartlist_get(args,2));
+ char *purp = smartlist_get(args,2);
+ if (get_purpose(&purp, 1, &intended_purpose) < 0) {
+ connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
smartlist_free(args);
goto done;
@@ -1717,6 +1766,11 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
if (!zero_circ && !circ) {
goto done;
}
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ if (!zero_circ && !circ) {
+ goto done;
+ }
}
routers = smartlist_create();
@@ -1742,7 +1796,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
if (zero_circ) {
/* start a new circuit */
- circ = circuit_init(intended_purpose, 0, 0, 0);
+ circ = origin_circuit_init(intended_purpose, 0, 0, 0);
}
/* now circ refers to something that is ready to be extended */
@@ -1756,7 +1810,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
/* now that we've populated the cpath, start extending */
if (zero_circ) {
if (circuit_handle_first_hop(circ) < 0) {
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
if (v0)
send_control0_error(conn, ERR_INTERNAL, "couldn't start circuit");
else
@@ -1764,12 +1818,12 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
goto done;
}
} else {
- if (circ->state == CIRCUIT_STATE_OPEN) {
- circuit_set_state(circ, CIRCUIT_STATE_BUILDING);
+ if (circ->_base.state == CIRCUIT_STATE_OPEN) {
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
if (circuit_send_next_onion_skin(circ) < 0) {
log_info(LD_CONTROL,
"send_next_onion_skin failed; circuit marked for closing.");
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
if (v0)
send_control0_error(conn, ERR_INTERNAL, "couldn't send onion skin");
else
@@ -1798,13 +1852,14 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
* is 1) or SETROUTERPURPOSE message. If we can find
* the circuit/router and it's a valid purpose, change it. */
static int
-handle_control_setpurpose(connection_t *conn, int for_circuits,
+handle_control_setpurpose(control_connection_t *conn, int for_circuits,
uint32_t len, const char *body)
{
- circuit_t *circ = NULL;
+ origin_circuit_t *circ = NULL;
routerinfo_t *ri = NULL;
uint8_t new_purpose;
smartlist_t *args = smartlist_create();
+ (void) len; /* body is nul-terminated, so it's safe to ignore the length. */
smartlist_split_string(args, body, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
if (smartlist_len(args)<2) {
@@ -1828,14 +1883,16 @@ handle_control_setpurpose(connection_t *conn, int for_circuits,
}
}
- if (get_purpose(smartlist_get(args,1), for_circuits, &new_purpose) < 0) {
- connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
- (char *)smartlist_get(args,1));
- goto done;
+ {
+ char *purp = smartlist_get(args,1);
+ if (get_purpose(&purp, for_circuits, &new_purpose) < 0) {
+ connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
+ goto done;
+ }
}
if (for_circuits)
- circ->purpose = new_purpose;
+ circ->_base.purpose = new_purpose;
else
ri->purpose = new_purpose;
connection_write_str_to_buf("250 OK\r\n", conn);
@@ -1849,14 +1906,14 @@ done:
/** Called when we get an ATTACHSTREAM message. Try to attach the requested
* stream, and report success or failure. */
static int
-handle_control_attachstream(connection_t *conn, uint32_t len,
+handle_control_attachstream(control_connection_t *conn, uint32_t len,
const char *body)
{
- connection_t *ap_conn = NULL;
- circuit_t *circ = NULL;
+ edge_connection_t *ap_conn = NULL;
+ origin_circuit_t *circ = NULL;
int zero_circ;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
uint32_t conn_id;
uint32_t circ_id;
if (len < 8) {
@@ -1905,10 +1962,10 @@ handle_control_attachstream(connection_t *conn, uint32_t len,
return 0;
}
- if (ap_conn->state != AP_CONN_STATE_CONTROLLER_WAIT &&
- ap_conn->state != AP_CONN_STATE_CONNECT_WAIT &&
- ap_conn->state != AP_CONN_STATE_RESOLVE_WAIT) {
- if (STATE_IS_V0(conn->state)) {
+ if (ap_conn->_base.state != AP_CONN_STATE_CONTROLLER_WAIT &&
+ ap_conn->_base.state != AP_CONN_STATE_CONNECT_WAIT &&
+ ap_conn->_base.state != AP_CONN_STATE_RESOLVE_WAIT) {
+ if (STATE_IS_V0(conn->_base.state)) {
send_control0_error(conn, ERR_NO_STREAM,
"Connection is not managed by controller.");
} else {
@@ -1920,28 +1977,29 @@ handle_control_attachstream(connection_t *conn, uint32_t len,
}
/* Do we need to detach it first? */
- if (ap_conn->state != AP_CONN_STATE_CONTROLLER_WAIT) {
+ if (ap_conn->_base.state != AP_CONN_STATE_CONTROLLER_WAIT) {
circuit_t *tmpcirc = circuit_get_by_edge_conn(ap_conn);
- connection_edge_end(ap_conn, END_STREAM_REASON_TIMEOUT, conn->cpath_layer);
+ connection_edge_end(ap_conn, END_STREAM_REASON_TIMEOUT,
+ ap_conn->cpath_layer);
/* Un-mark it as ending, since we're going to reuse it. */
- ap_conn->has_sent_end = 0;
+ ap_conn->_base.edge_has_sent_end = 0;
if (tmpcirc)
circuit_detach_stream(tmpcirc,ap_conn);
- ap_conn->state = AP_CONN_STATE_CONTROLLER_WAIT;
+ ap_conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
}
- if (circ && circ->state != CIRCUIT_STATE_OPEN) {
- if (STATE_IS_V0(conn->state))
+ if (circ && (circ->_base.state != CIRCUIT_STATE_OPEN)) {
+ if (STATE_IS_V0(conn->_base.state))
send_control0_error(conn, ERR_INTERNAL,
- "Refuse to attach stream to non-open circ.");
+ "Refuse to attach stream to non-open, origin circ.");
else
connection_write_str_to_buf(
- "551 Can't attach stream to non-open circuit\r\n",
- conn);
+ "551 Can't attach stream to non-open, origin circuit\r\n",
+ conn);
return 0;
}
if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ) < 0) {
- if (STATE_IS_V0(conn->state))
+ if (STATE_IS_V0(conn->_base.state))
send_control0_error(conn, ERR_INTERNAL, "Unable to attach stream.");
else
connection_write_str_to_buf("551 Unable to attach stream\r\n", conn);
@@ -1954,11 +2012,11 @@ handle_control_attachstream(connection_t *conn, uint32_t len,
/** Called when we get a POSTDESCRIPTOR message. Try to learn the provided
* descriptor, and report success or failure. */
static int
-handle_control_postdescriptor(connection_t *conn, uint32_t len,
+handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
const char *body)
{
char *desc;
- int v0 = STATE_IS_V0(conn->state);
+ int v0 = STATE_IS_V0(conn->_base.state);
const char *msg=NULL;
uint8_t purpose = ROUTER_PURPOSE_GENERAL;
@@ -1972,9 +2030,10 @@ handle_control_postdescriptor(connection_t *conn, uint32_t len,
smartlist_split_string(args, body, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
if (smartlist_len(args)) {
- if (get_purpose(smartlist_get(args,0), 0, &purpose) < 0) {
+ char *purp = smartlist_get(args,0);
+ if (get_purpose(&purp, 0, &purpose) < 0) {
connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n",
- (char *)smartlist_get(args,0));
+ purp);
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
smartlist_free(args);
return 0;
@@ -2013,14 +2072,14 @@ handle_control_postdescriptor(connection_t *conn, uint32_t len,
/** Called when we receive a REDIRECTSTERAM command. Try to change the target
* address of the named AP stream, and report success or failure. */
static int
-handle_control_redirectstream(connection_t *conn, uint32_t len,
+handle_control_redirectstream(control_connection_t *conn, uint32_t len,
const char *body)
{
- connection_t *ap_conn = NULL;
+ edge_connection_t *ap_conn = NULL;
uint32_t conn_id;
char *new_addr = NULL;
uint16_t new_port = 0;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
if (len < 6) {
send_control0_error(conn, ERR_SYNTAX,
"redirectstream message too short");
@@ -2029,8 +2088,8 @@ handle_control_redirectstream(connection_t *conn, uint32_t len,
conn_id = ntohl(get_uint32(body));
if (!(ap_conn = connection_get_by_global_id(conn_id))
- || ap_conn->state != CONN_TYPE_AP
- || !ap_conn->socks_request) {
+ || ap_conn->_base.state != CONN_TYPE_AP
+ || ap_conn->socks_request) {
send_control0_error(conn, ERR_NO_STREAM,
"No AP connection found with given ID");
return 0;
@@ -2080,13 +2139,13 @@ handle_control_redirectstream(connection_t *conn, uint32_t len,
/** Called when we get a CLOSESTREAM command; try to close the named stream
* and report success or failure. */
static int
-handle_control_closestream(connection_t *conn, uint32_t len,
+handle_control_closestream(control_connection_t *conn, uint32_t len,
const char *body)
{
- connection_t *ap_conn=NULL;
+ edge_connection_t *ap_conn=NULL;
uint8_t reason=0;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
uint32_t conn_id;
if (len < 6) {
send_control0_error(conn, ERR_SYNTAX, "closestream message too short");
@@ -2097,8 +2156,8 @@ handle_control_closestream(connection_t *conn, uint32_t len,
reason = *(uint8_t*)(body+4);
if (!(ap_conn = connection_get_by_global_id(conn_id))
- || ap_conn->state != CONN_TYPE_AP
- || !ap_conn->socks_request) {
+ || ap_conn->_base.state != CONN_TYPE_AP
+ || ap_conn->socks_request) {
send_control0_error(conn, ERR_NO_STREAM,
"No AP connection found with given ID");
return 0;
@@ -2138,13 +2197,13 @@ handle_control_closestream(connection_t *conn, uint32_t len,
/** Called when we get a CLOSECIRCUIT command; try to close the named circuit
* and report success or failure. */
static int
-handle_control_closecircuit(connection_t *conn, uint32_t len,
+handle_control_closecircuit(control_connection_t *conn, uint32_t len,
const char *body)
{
- circuit_t *circ = NULL;
+ origin_circuit_t *circ = NULL;
int safe = 0;
- if (STATE_IS_V0(conn->state)) {
+ if (STATE_IS_V0(conn->_base.state)) {
uint32_t circ_id;
if (len < 5) {
send_control0_error(conn, ERR_SYNTAX, "closecircuit message too short");
@@ -2186,7 +2245,7 @@ handle_control_closecircuit(connection_t *conn, uint32_t len,
}
if (!safe || !circ->p_streams) {
- circuit_mark_for_close(circ, END_CIRC_REASON_NONE);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NONE);
}
send_control_done(conn);
@@ -2199,7 +2258,7 @@ handle_control_closecircuit(connection_t *conn, uint32_t len,
* fragments and report failure.
*/
static int
-handle_control_fragments(connection_t *conn, uint16_t command_type,
+handle_control_fragments(control_connection_t *conn, uint16_t command_type,
uint32_t body_len, char *body)
{
if (command_type == CONTROL0_CMD_FRAGMENTHEADER) {
@@ -2240,24 +2299,22 @@ handle_control_fragments(connection_t *conn, uint16_t command_type,
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
-connection_control_finished_flushing(connection_t *conn)
+connection_control_finished_flushing(control_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_CONTROL);
- connection_stop_writing(conn);
+ connection_stop_writing(TO_CONN(conn));
return 0;
}
/** Called when <b>conn</b> has gotten its socket closed. */
int
-connection_control_reached_eof(connection_t *conn)
+connection_control_reached_eof(control_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_CONTROL);
log_info(LD_CONTROL,"Control connection reached EOF. Closing.");
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return 0;
}
@@ -2265,16 +2322,15 @@ connection_control_reached_eof(connection_t *conn)
* commands from conn->inbuf, and execute them.
*/
static int
-connection_control_process_inbuf_v1(connection_t *conn)
+connection_control_process_inbuf_v1(control_connection_t *conn)
{
size_t data_len;
int cmd_len;
char *args;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_CONTROL);
- tor_assert(conn->state == CONTROL_CONN_STATE_OPEN_V1 ||
- conn->state == CONTROL_CONN_STATE_NEEDAUTH_V1);
+ tor_assert(conn->_base.state == CONTROL_CONN_STATE_OPEN_V1 ||
+ conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH_V1);
if (!conn->incoming_cmd) {
conn->incoming_cmd = tor_malloc(1024);
@@ -2289,7 +2345,7 @@ connection_control_process_inbuf_v1(connection_t *conn)
/* First, fetch a line. */
do {
data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len;
- r = fetch_from_buf_line(conn->inbuf,
+ r = fetch_from_buf_line(conn->_base.inbuf,
conn->incoming_cmd+conn->incoming_cmd_cur_len,
&data_len);
if (r == 0)
@@ -2339,11 +2395,11 @@ connection_control_process_inbuf_v1(connection_t *conn)
if (!strcasecmp(conn->incoming_cmd, "QUIT")) {
connection_write_str_to_buf("250 closing connection\r\n", conn);
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return 0;
}
- if (conn->state == CONTROL_CONN_STATE_NEEDAUTH_V1 &&
+ if (conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH_V1 &&
strcasecmp(conn->incoming_cmd, "AUTHENTICATE")) {
connection_write_str_to_buf("514 Authentication required.\r\n", conn);
conn->incoming_cmd_cur_len = 0;
@@ -2414,7 +2470,7 @@ connection_control_process_inbuf_v1(connection_t *conn)
* commands from conn->inbuf, and execute them.
*/
static int
-connection_control_process_inbuf_v0(connection_t *conn)
+connection_control_process_inbuf_v0(control_connection_t *conn)
{
uint32_t body_len;
uint16_t command_type;
@@ -2422,15 +2478,16 @@ connection_control_process_inbuf_v0(connection_t *conn)
again:
/* Try to suck a control message from the buffer. */
- switch (fetch_from_buf_control0(conn->inbuf, &body_len, &command_type, &body,
- conn->state == CONTROL_CONN_STATE_NEEDAUTH_V0))
+ switch (fetch_from_buf_control0(conn->_base.inbuf, &body_len, &command_type,
+ &body,
+ conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH_V0))
{
case -2:
tor_free(body);
log_info(LD_CONTROL,
"Detected v1 control protocol on connection (fd %d)",
- conn->s);
- conn->state = CONTROL_CONN_STATE_NEEDAUTH_V1;
+ conn->_base.s);
+ conn->_base.state = CONTROL_CONN_STATE_NEEDAUTH_V1;
return connection_control_process_inbuf_v1(conn);
case -1:
tor_free(body);
@@ -2448,7 +2505,7 @@ connection_control_process_inbuf_v0(connection_t *conn)
/* We got a command. If we need authentication, only authentication
* commands will be considered. */
- if (conn->state == CONTROL_CONN_STATE_NEEDAUTH_V0 &&
+ if (conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH_V0 &&
command_type != CONTROL0_CMD_AUTHENTICATE) {
log_info(LD_CONTROL, "Rejecting '%s' command; authentication needed.",
control_cmd_to_string(command_type));
@@ -2562,12 +2619,11 @@ connection_control_process_inbuf_v0(connection_t *conn)
/** Called when <b>conn</b> has received more bytes on its inbuf.
*/
int
-connection_control_process_inbuf(connection_t *conn)
+connection_control_process_inbuf(control_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_CONTROL);
- if (STATE_IS_V0(conn->state))
+ if (STATE_IS_V0(conn->_base.state))
return connection_control_process_inbuf_v0(conn);
else
return connection_control_process_inbuf_v1(conn);
@@ -2576,13 +2632,12 @@ connection_control_process_inbuf(connection_t *conn)
/** Something has happened to circuit <b>circ</b>: tell any interested
* control connections. */
int
-control_event_circuit_status(circuit_t *circ, circuit_status_event_t tp)
+control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp)
{
char *path, *msg;
if (!EVENT_IS_INTERESTING(EVENT_CIRCUIT_STATUS))
return 0;
tor_assert(circ);
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
path = circuit_list_path(circ,0);
if (EVENT_IS_INTERESTING0(EVENT_CIRCUIT_STATUS)) {
@@ -2623,7 +2678,7 @@ control_event_circuit_status(circuit_t *circ, circuit_status_event_t tp)
* <b>conn</b>, and write it to <b>buf</b>. Return 0 on success, -1 on
* failure. */
static int
-write_stream_target_to_buf(connection_t *conn, char *buf, size_t len)
+write_stream_target_to_buf(edge_connection_t *conn, char *buf, size_t len)
{
char buf2[256];
if (conn->chosen_exit_name)
@@ -2642,12 +2697,11 @@ write_stream_target_to_buf(connection_t *conn, char *buf, size_t len)
/** Something has happened to the stream associated with AP connection
* <b>conn</b>: tell any interested control connections. */
int
-control_event_stream_status(connection_t *conn, stream_status_event_t tp)
+control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp)
{
char *msg;
size_t len;
char buf[256];
- tor_assert(conn->type == CONN_TYPE_AP);
tor_assert(conn->socks_request);
if (!EVENT_IS_INTERESTING(EVENT_STREAM_STATUS))
@@ -2667,6 +2721,7 @@ control_event_stream_status(connection_t *conn, stream_status_event_t tp)
if (EVENT_IS_INTERESTING1(EVENT_STREAM_STATUS)) {
const char *status;
circuit_t *circ;
+ origin_circuit_t *origin_circ = NULL;
switch (tp)
{
case STREAM_EVENT_SENT_CONNECT: status = "SENTCONNECT"; break;
@@ -2682,10 +2737,13 @@ control_event_stream_status(connection_t *conn, stream_status_event_t tp)
return 0;
}
circ = circuit_get_by_edge_conn(conn);
+ if (circ && CIRCUIT_IS_ORIGIN(circ))
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
send_control1_event(EVENT_STREAM_STATUS,
"650 STREAM %lu %s %lu %s\r\n",
(unsigned long)conn->global_identifier, status,
- circ?(unsigned long)circ->global_identifier : 0ul,
+ origin_circ?
+ (unsigned long)origin_circ->global_identifier : 0ul,
buf);
/* XXX need to specify its intended exit, etc? */
}
@@ -2695,13 +2753,11 @@ control_event_stream_status(connection_t *conn, stream_status_event_t tp)
/** Something has happened to the OR connection <b>conn</b>: tell any
* interested control connections. */
int
-control_event_or_conn_status(connection_t *conn,or_conn_status_event_t tp)
+control_event_or_conn_status(or_connection_t *conn,or_conn_status_event_t tp)
{
char buf[HEX_DIGEST_LEN+3]; /* status, dollar, identity, NUL */
size_t len;
- tor_assert(conn->type == CONN_TYPE_OR);
-
if (!EVENT_IS_INTERESTING(EVENT_OR_CONN_STATUS))
return 0;
@@ -2718,13 +2774,14 @@ control_event_or_conn_status(connection_t *conn,or_conn_status_event_t tp)
strlcpy(name, conn->nickname, sizeof(name));
else
tor_snprintf(name, sizeof(name), "%s:%d",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
switch (tp)
{
case OR_CONN_EVENT_LAUNCHED: status = "LAUNCHED"; break;
case OR_CONN_EVENT_CONNECTED: status = "CONNECTED"; break;
case OR_CONN_EVENT_FAILED: status = "FAILED"; break;
case OR_CONN_EVENT_CLOSED: status = "CLOSED"; break;
+ case OR_CONN_EVENT_NEW: status = "NEW"; break;
default:
log_warn(LD_BUG, "Unrecognized status code %d", (int)tp);
return 0;
@@ -2781,6 +2838,7 @@ void
control_event_logmsg(int severity, unsigned int domain, const char *msg)
{
int oldlog, event;
+ (void) domain;
if (disable_log_messages)
return;
@@ -2858,8 +2916,12 @@ control_event_descriptors_changed(smartlist_t *routers)
tor_free(msg);
}
if (EVENT_IS_INTERESTING1(EVENT_NEW_DESC)) {
- msg = smartlist_join_strings(identities, " ", 0, &len);
- send_control1_event(EVENT_NEW_DESC, "650 NEWDESC %s\r\n", msg);
+ char *ids = smartlist_join_strings(identities, " ", 0, &len);
+ size_t len = strlen(ids)+32;
+ msg = tor_malloc(len);
+ tor_snprintf(msg, len, "650 NEWDESC %s\r\n", ids);
+ send_control1_event_string(EVENT_NEW_DESC, msg);
+ tor_free(ids);
tor_free(msg);
}
SMARTLIST_FOREACH(identities, char *, cp, tor_free(cp));
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index bece3456bc..17c176ae11 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -36,7 +36,7 @@ static int num_cpuworkers_busy=0;
* the last time we got a key rotation event. */
static time_t last_rotation_time=0;
-static int cpuworker_main(void *data);
+static void cpuworker_main(void *data);
static int spawn_cpuworker(void);
static void spawn_enough_cpuworkers(void);
static void process_pending_task(connection_t *cpuworker);
@@ -137,7 +137,7 @@ connection_cpu_process_inbuf(connection_t *conn)
uint32_t addr;
uint16_t port;
uint16_t circ_id;
- connection_t *p_conn;
+ or_connection_t *p_conn;
circuit_t *circ;
tor_assert(conn);
@@ -181,8 +181,8 @@ connection_cpu_process_inbuf(connection_t *conn)
log_debug(LD_OR,"processed onion for a circ that's gone. Dropping.");
goto done_processing;
}
- tor_assert(circ->p_conn);
- if (onionskin_answer(circ, CELL_CREATED, buf+TAG_LEN,
+ tor_assert(! CIRCUIT_IS_ORIGIN(circ));
+ if (onionskin_answer(TO_OR_CIRCUIT(circ), CELL_CREATED, buf+TAG_LEN,
buf+TAG_LEN+ONIONSKIN_REPLY_LEN) < 0) {
log_warn(LD_OR,"onionskin_answer failed. Closing.");
circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
@@ -222,7 +222,7 @@ done_processing:
* (Note: this _should_ be by addr/port, since we're concerned with specific
* connections, not with routers (where we'd use identity).)
*/
-static int
+static void
cpuworker_main(void *data)
{
char question[ONIONSKIN_CHALLENGE_LEN];
@@ -308,7 +308,6 @@ cpuworker_main(void *data)
tor_close_socket(fd);
crypto_thread_cleanup();
spawn_exit();
- return 0; /* windows wants this function to return an int */
}
/** Launch a new cpuworker. Return 0 if we're happy, -1 if we failed.
@@ -329,6 +328,9 @@ spawn_cpuworker(void)
return -1;
}
+ tor_assert(fdarray[0] >= 0);
+ tor_assert(fdarray[1] >= 0);
+
fd = fdarray[0];
spawn_func(cpuworker_main, (void*)fdarray);
log_debug(LD_OR,"just spawned a cpu worker.");
@@ -383,7 +385,7 @@ spawn_enough_cpuworkers(void)
static void
process_pending_task(connection_t *cpuworker)
{
- circuit_t *circ;
+ or_circuit_t *circ;
tor_assert(cpuworker);
@@ -441,7 +443,7 @@ int
assign_to_cpuworker(connection_t *cpuworker, uint8_t question_type,
void *task)
{
- circuit_t *circ;
+ or_circuit_t *circ;
char tag[TAG_LEN];
tor_assert(question_type == CPUWORKER_TASK_ONION);
@@ -451,7 +453,7 @@ assign_to_cpuworker(connection_t *cpuworker, uint8_t question_type,
if (question_type == CPUWORKER_TASK_ONION) {
circ = task;
- tor_assert(circ->onionskin);
+ tor_assert(circ->_base.onionskin);
if (num_cpuworkers_busy == num_cpuworkers) {
log_debug(LD_OR,"No idle cpuworkers. Queuing.");
@@ -470,7 +472,8 @@ assign_to_cpuworker(connection_t *cpuworker, uint8_t question_type,
log_info(LD_OR,"circ->p_conn gone. Failing circ.");
return -1;
}
- tag_pack(tag, circ->p_conn->addr, circ->p_conn->port, circ->p_circ_id);
+ tag_pack(tag, circ->p_conn->_base.addr, circ->p_conn->_base.port,
+ circ->p_circ_id);
cpuworker->state = CPUWORKER_STATE_BUSY_ONION;
/* touch the lastwritten timestamp, since that's how we check to
@@ -481,9 +484,9 @@ assign_to_cpuworker(connection_t *cpuworker, uint8_t question_type,
connection_write_to_buf((char*)&question_type, 1, cpuworker);
connection_write_to_buf(tag, sizeof(tag), cpuworker);
- connection_write_to_buf(circ->onionskin, ONIONSKIN_CHALLENGE_LEN,
+ connection_write_to_buf(circ->_base.onionskin, ONIONSKIN_CHALLENGE_LEN,
cpuworker);
- tor_free(circ->onionskin);
+ tor_free(circ->_base.onionskin);
}
return 0;
}
diff --git a/src/or/directory.c b/src/or/directory.c
index 3a3f3a0012..91b5322bcf 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -38,16 +38,17 @@ directory_initiate_command(const char *address, uint32_t addr, uint16_t port,
const char *payload, size_t payload_len);
static void
-directory_send_command(connection_t *conn, const char *platform,
+directory_send_command(dir_connection_t *conn, const char *platform,
int purpose, const char *resource,
const char *payload, size_t payload_len);
-static int directory_handle_command(connection_t *conn);
+static int directory_handle_command(dir_connection_t *conn);
static int body_is_plausible(const char *body, size_t body_len, int purpose);
static int purpose_is_private(uint8_t purpose);
static char *http_get_header(const char *headers, const char *which);
-static char *http_get_origin(const char *headers, connection_t *conn);
-static void connection_dir_download_networkstatus_failed(connection_t *conn);
-static void connection_dir_download_routerdesc_failed(connection_t *conn);
+static void http_set_address_origin(const char *headers, connection_t *conn);
+static void connection_dir_download_networkstatus_failed(
+ dir_connection_t *conn);
+static void connection_dir_download_routerdesc_failed(dir_connection_t *conn);
static void dir_networkstatus_download_failed(smartlist_t *failed);
static void dir_routerdesc_download_failed(smartlist_t *failed);
static void note_request(const char *key, size_t bytes);
@@ -58,6 +59,8 @@ static void note_request(const char *key, size_t bytes);
* before deciding that one of us has the wrong time? */
#define ALLOW_DIRECTORY_TIME_SKEW (30*60)
+#define X_ADDRESS_HEADER "X-Your-Address-Is: "
+
/********* END VARIABLES ************/
/** Return true iff the directory purpose 'purpose' must use an
@@ -238,7 +241,7 @@ directory_initiate_command_routerstatus(routerstatus_t *status,
{
const char *platform = NULL;
routerinfo_t *router;
- char address_buf[INET_NTOA_BUF_LEN];
+ char address_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
const char *address;
if ((router = router_get_by_digest(status->identity_digest))) {
@@ -259,24 +262,24 @@ directory_initiate_command_routerstatus(routerstatus_t *status,
* directory server: Mark the router as down and try again if possible.
*/
void
-connection_dir_request_failed(connection_t *conn)
+connection_dir_request_failed(dir_connection_t *conn)
{
if (router_digest_is_me(conn->identity_digest))
return; /* this was a test fetch. don't retry. */
router_set_status(conn->identity_digest, 0); /* don't try him again */
- if (conn->purpose == DIR_PURPOSE_FETCH_DIR ||
- conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_DIR ||
+ conn->_base.purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
log_info(LD_DIR, "Giving up on directory server at '%s:%d'; retrying",
- conn->address, conn->port);
- directory_get_from_dirserver(conn->purpose, NULL,
+ conn->_base.address, conn->_base.port);
+ directory_get_from_dirserver(conn->_base.purpose, NULL,
0 /* don't retry_if_no_servers */);
- } else if (conn->purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
+ } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
- conn->address);
+ conn->_base.address);
connection_dir_download_networkstatus_failed(conn);
- } else if (conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC) {
+ } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC) {
log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
- conn->address);
+ conn->_base.address);
connection_dir_download_routerdesc_failed(conn);
}
}
@@ -286,7 +289,7 @@ connection_dir_request_failed(connection_t *conn)
* retry the fetch now, later, or never.
*/
static void
-connection_dir_download_networkstatus_failed(connection_t *conn)
+connection_dir_download_networkstatus_failed(dir_connection_t *conn)
{
if (!conn->requested_resource) {
/* We never reached directory_send_command, which means that we never
@@ -299,14 +302,14 @@ connection_dir_download_networkstatus_failed(connection_t *conn)
smartlist_t *trusted_dirs = router_get_trusted_dir_servers();
SMARTLIST_FOREACH(trusted_dirs, trusted_dir_server_t *, ds,
++ds->n_networkstatus_failures);
- directory_get_from_dirserver(conn->purpose, "all.z",
+ directory_get_from_dirserver(conn->_base.purpose, "all.z",
0 /* don't retry_if_no_servers */);
} else if (!strcmpstart(conn->requested_resource, "fp/")) {
/* We were trying to download by fingerprint; mark them all as having
* failed, and possibly retry them later.*/
smartlist_t *failed = smartlist_create();
dir_split_resource_into_fingerprints(conn->requested_resource+3,
- failed, NULL, 0);
+ failed, NULL, 0, 0);
if (smartlist_len(failed)) {
dir_networkstatus_download_failed(failed);
SMARTLIST_FOREACH(failed, char *, cp, tor_free(cp));
@@ -319,11 +322,13 @@ connection_dir_download_networkstatus_failed(connection_t *conn)
* on connection <b>conn</b> failed.
*/
static void
-connection_dir_download_routerdesc_failed(connection_t *conn)
+connection_dir_download_routerdesc_failed(dir_connection_t *conn)
{
/* Try again. No need to increment the failure count for routerdescs, since
* it's not their fault.*/
/* update_router_descriptor_downloads(time(NULL)); */
+ (void) conn;
+ /* XXXX Why did the above get commented out? -NM */
}
/** Helper for directory_initiate_command_(router|trusted_dir): send the
@@ -338,7 +343,7 @@ directory_initiate_command(const char *address, uint32_t addr,
int private_connection, const char *resource,
const char *payload, size_t payload_len)
{
- connection_t *conn;
+ dir_connection_t *conn;
tor_assert(address);
tor_assert(addr);
@@ -372,18 +377,19 @@ directory_initiate_command(const char *address, uint32_t addr,
tor_assert(0);
}
- conn = connection_new(CONN_TYPE_DIR);
+ conn = TO_DIR_CONN(connection_new(CONN_TYPE_DIR));
/* set up conn so it's got all the data we need to remember */
- conn->addr = addr;
- conn->port = dir_port;
- conn->address = tor_strdup(address);
+ conn->_base.addr = addr;
+ conn->_base.port = dir_port;
+ conn->_base.address = tor_strdup(address);
memcpy(conn->identity_digest, digest, DIGEST_LEN);
- conn->purpose = purpose;
+ conn->_base.purpose = purpose;
/* give it an initial state */
- conn->state = DIR_CONN_STATE_CONNECTING;
+ conn->_base.state = DIR_CONN_STATE_CONNECTING;
+ conn->dirconn_direct = (private_connection == 0);
if (!private_connection) {
/* then we want to connect directly */
@@ -393,19 +399,21 @@ directory_initiate_command(const char *address, uint32_t addr,
dir_port = get_options()->HttpProxyPort;
}
- switch (connection_connect(conn, conn->address, addr, dir_port)) {
+ switch (connection_connect(TO_CONN(conn), conn->_base.address, addr,
+ dir_port)) {
case -1:
connection_dir_request_failed(conn); /* retry if we want */
- connection_free(conn);
+ connection_free(TO_CONN(conn));
return;
case 1:
- conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
+ /* start flushing conn */
+ conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING;
/* fall through */
case 0:
/* queue the command on the outbuf */
directory_send_command(conn, platform, purpose, resource,
payload, payload_len);
- connection_watch_events(conn, EV_READ | EV_WRITE);
+ connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE);
/* writable indicates finish, readable indicates broken link,
error indicates broken link in windowsland. */
}
@@ -414,23 +422,24 @@ directory_initiate_command(const char *address, uint32_t addr,
* populate it and add it at the right state
* socketpair and hook up both sides
*/
- conn->s = connection_ap_make_bridge(conn->address, conn->port);
- if (conn->s < 0) {
+ conn->_base.s = connection_ap_make_bridge(conn->_base.address,
+ conn->_base.port);
+ if (conn->_base.s < 0) {
log_warn(LD_NET,"Making AP bridge to dirserver failed.");
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return;
}
- if (connection_add(conn) < 0) {
+ if (connection_add(TO_CONN(conn)) < 0) {
log_warn(LD_NET,"Unable to add AP bridge to dirserver.");
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return;
}
- conn->state = DIR_CONN_STATE_CLIENT_SENDING;
+ conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING;
/* queue the command on the outbuf */
directory_send_command(conn, platform, purpose, resource,
payload, payload_len);
- connection_watch_events(conn, EV_READ | EV_WRITE);
+ connection_watch_events(TO_CONN(conn), EV_READ | EV_WRITE);
}
}
@@ -438,7 +447,7 @@ directory_initiate_command(const char *address, uint32_t addr,
* are as in directory_initiate_command.
*/
static void
-directory_send_command(connection_t *conn, const char *platform,
+directory_send_command(dir_connection_t *conn, const char *platform,
int purpose, const char *resource,
const char *payload, size_t payload_len)
{
@@ -451,18 +460,18 @@ directory_send_command(connection_t *conn, const char *platform,
size_t len;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_DIR);
+ tor_assert(conn->_base.type == CONN_TYPE_DIR);
tor_free(conn->requested_resource);
if (resource)
conn->requested_resource = tor_strdup(resource);
/* come up with a string for which Host: we want */
- if (conn->port == 80) {
- strlcpy(hoststring, conn->address, sizeof(hoststring));
+ if (conn->_base.port == 80) {
+ strlcpy(hoststring, conn->_base.address, sizeof(hoststring));
} else {
tor_snprintf(hoststring, sizeof(hoststring),"%s:%d",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
}
/* come up with some proxy lines, if we're using one. */
@@ -559,8 +568,8 @@ directory_send_command(connection_t *conn, const char *platform,
}
tor_snprintf(request, sizeof(request), "%s %s", httpcommand, proxystring);
- connection_write_to_buf(request, strlen(request), conn);
- connection_write_to_buf(url, strlen(url), conn);
+ connection_write_to_buf(request, strlen(request), TO_CONN(conn));
+ connection_write_to_buf(url, strlen(url), TO_CONN(conn));
tor_free(url);
if (!strcmp(httpcommand, "GET") && !payload) {
@@ -575,11 +584,11 @@ directory_send_command(connection_t *conn, const char *platform,
hoststring,
proxyauthstring);
}
- connection_write_to_buf(request, strlen(request), conn);
+ connection_write_to_buf(request, strlen(request), TO_CONN(conn));
if (payload) {
/* then send the payload afterwards too */
- connection_write_to_buf(payload, payload_len, conn);
+ connection_write_to_buf(payload, payload_len, TO_CONN(conn));
}
}
@@ -588,7 +597,7 @@ directory_send_command(connection_t *conn, const char *platform,
* "\%s [http[s]://]\%s HTTP/1..."
* \endverbatim
* If it's well-formed, strdup the second \%s into *<b>url</b>, and
- * null-terminate it. If the url doesn't start with "/tor/", rewrite it
+ * nul-terminate it. If the url doesn't start with "/tor/", rewrite it
* so it does. Return 0.
* Otherwise, return -1.
*/
@@ -655,12 +664,11 @@ http_get_header(const char *headers, const char *which)
return NULL;
}
-/** Allocate and return a string describing the source of an HTTP request with
- * headers <b>headers</b> received on <b>conn</b>. The format is either
- * "'1.2.3.4'", or "'1.2.3.4' (forwarded for '5.6.7.8')".
- */
-static char *
-http_get_origin(const char *headers, connection_t *conn)
+/** If <b>headers</b> indicates that a proxy was involved, then rewrite
+ * <b>conn</b>-\>address to describe our best guess of the address that
+ * originated this HTTP request. */
+static void
+http_set_address_origin(const char *headers, connection_t *conn)
{
char *fwd;
@@ -668,17 +676,9 @@ http_get_origin(const char *headers, connection_t *conn)
if (!fwd)
fwd = http_get_header(headers, "X-Forwarded-For: ");
if (fwd) {
- size_t len = strlen(fwd)+strlen(conn->address)+32;
- char *result = tor_malloc(len);
- tor_snprintf(result, len, "'%s' (forwarded for %s)", conn->address,
- escaped(fwd));
+ tor_free(conn->address);
+ conn->address = tor_strdup(escaped(fwd));
tor_free(fwd);
- return result;
- } else {
- size_t len = strlen(conn->address)+3;
- char *result = tor_malloc(len);
- tor_snprintf(result, len, "'%s'", conn->address);
- return result;
}
}
@@ -808,7 +808,7 @@ body_is_plausible(const char *body, size_t len, int purpose)
* The caller will take care of marking the connection for close.
*/
static int
-connection_dir_client_reached_eof(connection_t *conn)
+connection_dir_client_reached_eof(dir_connection_t *conn)
{
char *body;
char *headers;
@@ -820,17 +820,18 @@ connection_dir_client_reached_eof(connection_t *conn)
int compression;
int plausible;
int skewed=0;
- int allow_partial = conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC;
+ int allow_partial = conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC;
int was_compressed=0;
+ char *guess;
- switch (fetch_from_buf_http(conn->inbuf,
+ switch (fetch_from_buf_http(conn->_base.inbuf,
&headers, MAX_HEADERS_SIZE,
&body, &body_len, MAX_DIR_SIZE,
allow_partial)) {
case -1: /* overflow */
log_warn(LD_PROTOCOL,
"'fetch' response too large (server '%s:%d'). Closing.",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
return -1;
case 0:
log_info(LD_HTTP,
@@ -843,7 +844,7 @@ connection_dir_client_reached_eof(connection_t *conn)
if (parse_http_response(headers, &status_code, &date_header,
&compression, &reason) < 0) {
log_warn(LD_HTTP,"Unparseable headers (server '%s:%d'). Closing.",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
tor_free(body); tor_free(headers);
return -1;
}
@@ -851,7 +852,15 @@ connection_dir_client_reached_eof(connection_t *conn)
log_debug(LD_DIR,
"Received response from directory server '%s:%d': %d %s",
- conn->address, conn->port, status_code, escaped(reason));
+ conn->_base.address, conn->_base.port, status_code,
+ escaped(reason));
+
+ /* now check if it's got any hints for us about our IP address. */
+ guess = http_get_header(headers, X_ADDRESS_HEADER);
+ if (guess) {
+ router_new_address_suggestion(guess);
+ tor_free(guess);
+ }
if (date_header > 0) {
now = time(NULL);
@@ -862,7 +871,7 @@ connection_dir_client_reached_eof(connection_t *conn)
LD_HTTP,
"Received directory with skewed time (server '%s:%d'): "
"we are %d minutes %s, or the directory is %d minutes %s.",
- conn->address, conn->port,
+ conn->_base.address, conn->_base.port,
abs(delta)/60, delta>0 ? "ahead" : "behind",
abs(delta)/60, delta>0 ? "behind" : "ahead");
skewed = 1; /* don't check the recommended-versions line */
@@ -875,12 +884,13 @@ connection_dir_client_reached_eof(connection_t *conn)
if (status_code == 503) {
log_info(LD_DIR,"Received http status code %d (%s) from server "
"'%s:%d'. I'll try again soon.",
- status_code, escaped(reason), conn->address, conn->port);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
return -1;
}
- plausible = body_is_plausible(body, body_len, conn->purpose);
+ plausible = body_is_plausible(body, body_len, conn->_base.purpose);
if (compression || !plausible) {
char *new_body = NULL;
size_t new_len = 0;
@@ -907,7 +917,8 @@ connection_dir_client_reached_eof(connection_t *conn)
log_info(LD_HTTP, "HTTP body from server '%s:%d' was labeled %s, "
"but it seems to be %s.%s",
- conn->address, conn->port, description1, description2,
+ conn->_base.address, conn->_base.port, description1,
+ description2,
(compression>0 && guessed>0)?" Trying both.":"");
}
/* Try declared compression first if we can. */
@@ -924,7 +935,7 @@ connection_dir_client_reached_eof(connection_t *conn)
if (!plausible && !new_body) {
log_fn(LOG_PROTOCOL_WARN, LD_HTTP,
"Unable to decompress HTTP body (server '%s:%d').",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
return -1;
}
@@ -936,38 +947,40 @@ connection_dir_client_reached_eof(connection_t *conn)
}
}
- if (conn->purpose == DIR_PURPOSE_FETCH_DIR) {
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_DIR) {
/* fetch/process the directory to cache it. */
log_info(LD_DIR,"Received directory (size %d) from server '%s:%d'",
- (int)body_len, conn->address, conn->port);
+ (int)body_len, conn->_base.address, conn->_base.port);
if (status_code != 200) {
log_warn(LD_DIR,"Received http status code %d (%s) from server "
"'%s:%d'. I'll try again soon.",
- status_code, escaped(reason), conn->address, conn->port);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
return -1;
}
if (router_parse_directory(body) < 0) {
log_notice(LD_DIR,"I failed to parse the directory I fetched from "
- "'%s:%d'. Ignoring.", conn->address, conn->port);
+ "'%s:%d'. Ignoring.", conn->_base.address, conn->_base.port);
}
note_request(was_compressed?"dl/dir.z":"dl/dir", orig_len);
}
- if (conn->purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_RUNNING_LIST) {
/* just update our list of running routers, if this list is new info */
log_info(LD_DIR,"Received running-routers list (size %d)", (int)body_len);
if (status_code != 200) {
log_warn(LD_DIR,"Received http status code %d (%s) from server "
"'%s:%d'. I'll try again soon.",
- status_code, escaped(reason), conn->address, conn->port);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
return -1;
}
if (router_parse_runningrouters(body)<0) {
log_warn(LD_DIR,
"Bad running-routers from server '%s:%d'. I'll try again soon.",
- conn->address, conn->port);
+ conn->_base.address, conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
return -1;
}
@@ -975,17 +988,18 @@ connection_dir_client_reached_eof(connection_t *conn)
"dl/running-routers", orig_len);
}
- if (conn->purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS) {
smartlist_t *which = NULL;
+ int source;
char *cp;
log_info(LD_DIR,"Received networkstatus objects (size %d) from server "
- "'%s:%d'",(int) body_len, conn->address, conn->port);
+ "'%s:%d'",(int) body_len, conn->_base.address, conn->_base.port);
if (status_code != 200) {
log_warn(LD_DIR,
"Received http status code %d (%s) from server "
"'%s:%d' while fetching \"/tor/status/%s\". I'll try again soon.",
- status_code, escaped(reason), conn->address, conn->port,
- conn->requested_resource);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port, conn->requested_resource);
tor_free(body); tor_free(headers); tor_free(reason);
connection_dir_download_networkstatus_failed(conn);
return -1;
@@ -993,11 +1007,13 @@ connection_dir_client_reached_eof(connection_t *conn)
note_request(was_compressed?"dl/status.z":"dl/status", orig_len);
if (conn->requested_resource &&
!strcmpstart(conn->requested_resource,"fp/")) {
+ source = NS_FROM_DIR_BY_FP;
which = smartlist_create();
dir_split_resource_into_fingerprints(conn->requested_resource+3,
- which, NULL, 0);
+ which, NULL, 0, 0);
} else if (conn->requested_resource &&
!strcmpstart(conn->requested_resource, "all")) {
+ source = NS_FROM_DIR_ALL;
which = smartlist_create();
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
@@ -1006,6 +1022,11 @@ connection_dir_client_reached_eof(connection_t *conn)
base16_encode(cp, HEX_DIGEST_LEN+1, ds->digest, DIGEST_LEN);
smartlist_add(which, cp);
});
+ } else {
+ /* Can we even end up here? -- weasel*/
+ source = NS_FROM_DIR_BY_FP;
+ log_warn(LD_BUG, "we received a networkstatus but we did neither ask"
+ "for it by fp/ nor did we ask for all.");
}
cp = body;
while (*cp) {
@@ -1013,7 +1034,7 @@ connection_dir_client_reached_eof(connection_t *conn)
if (next)
next[1] = '\0';
/* learn from it, and then remove it from 'which' */
- if (router_set_networkstatus(cp, time(NULL), NS_FROM_DIR, which)<0)
+ if (router_set_networkstatus(cp, time(NULL), source, which)<0)
break;
if (next) {
next[1] = 'n';
@@ -1033,17 +1054,17 @@ connection_dir_client_reached_eof(connection_t *conn)
}
}
- if (conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC) {
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC) {
smartlist_t *which = NULL;
int n_asked_for = 0;
log_info(LD_DIR,"Received server info (size %d) from server '%s:%d'",
- (int)body_len, conn->address, conn->port);
+ (int)body_len, conn->_base.address, conn->_base.port);
note_request(was_compressed?"dl/server.z":"dl/server", orig_len);
if (conn->requested_resource &&
!strcmpstart(conn->requested_resource,"d/")) {
which = smartlist_create();
dir_split_resource_into_fingerprints(conn->requested_resource+2,
- which, NULL, 0);
+ which, NULL, 0, 0);
n_asked_for = smartlist_len(which);
}
if (status_code != 200) {
@@ -1054,8 +1075,8 @@ connection_dir_client_reached_eof(connection_t *conn)
log_fn(dir_okay ? LOG_INFO : LOG_WARN, LD_DIR,
"Received http status code %d (%s) from server '%s:%d' "
"while fetching \"/tor/server/%s\". I'll try again soon.",
- status_code, escaped(reason), conn->address, conn->port,
- conn->requested_resource);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port, conn->requested_resource);
if (!which) {
connection_dir_download_routerdesc_failed(conn);
} else {
@@ -1074,13 +1095,13 @@ connection_dir_client_reached_eof(connection_t *conn)
if (which || (conn->requested_resource &&
!strcmpstart(conn->requested_resource, "all"))) {
/* as we learn from them, we remove them from 'which' */
- router_load_routers_from_string(body, 0, which);
+ router_load_routers_from_string(body, SAVED_NOWHERE, which);
directory_info_has_arrived(time(NULL), 0);
}
if (which) { /* mark remaining ones as failed */
log_info(LD_DIR, "Received %d/%d routers requested from %s:%d",
n_asked_for-smartlist_len(which), n_asked_for,
- conn->address, (int)conn->port);
+ conn->_base.address, (int)conn->_base.port);
if (smartlist_len(which)) {
dir_routerdesc_download_failed(which);
}
@@ -1093,13 +1114,13 @@ connection_dir_client_reached_eof(connection_t *conn)
routerinfo_t *me = router_get_my_routerinfo();
if (me &&
router_digest_is_me(conn->identity_digest) &&
- me->addr == conn->addr &&
- me->dir_port == conn->port)
+ me->addr == conn->_base.addr &&
+ me->dir_port == conn->_base.port)
router_dirport_found_reachable();
}
}
- if (conn->purpose == DIR_PURPOSE_UPLOAD_DIR) {
+ if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_DIR) {
switch (status_code) {
case 200:
log_info(LD_GENERAL,"eof (status 200) after uploading server "
@@ -1108,7 +1129,7 @@ connection_dir_client_reached_eof(connection_t *conn)
case 400:
log_warn(LD_GENERAL,"http status 400 (%s) response from "
"dirserver '%s:%d'. Please correct.",
- escaped(reason), conn->address, conn->port);
+ escaped(reason), conn->_base.address, conn->_base.port);
break;
case 403:
log_warn(LD_GENERAL,
@@ -1116,19 +1137,21 @@ connection_dir_client_reached_eof(connection_t *conn)
"'%s:%d'. Is your clock skewed? Have you mailed us your key "
"fingerprint? Are you using the right key? Are you using a "
"private IP address? See http://tor.eff.org/doc/"
- "tor-doc-server.html",escaped(reason), conn->address, conn->port);
+ "tor-doc-server.html",escaped(reason), conn->_base.address,
+ conn->_base.port);
break;
default:
log_warn(LD_GENERAL,
"http status %d (%s) reason unexpected (server '%s:%d').",
- status_code, escaped(reason), conn->address, conn->port);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port);
break;
}
/* return 0 in all cases, since we don't want to mark any
* dirservers down just because they don't like us. */
}
- if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) {
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC) {
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
"(%s))",
(int)body_len, status_code, escaped(reason));
@@ -1140,7 +1163,7 @@ connection_dir_client_reached_eof(connection_t *conn)
* cleans it up */
} else {
/* success. notify pending connections about this. */
- conn->purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
+ conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
rend_client_desc_here(conn->rend_query);
}
break;
@@ -1156,12 +1179,13 @@ connection_dir_client_reached_eof(connection_t *conn)
default:
log_warn(LD_REND,"http status %d (%s) response unexpected (server "
"'%s:%d').",
- status_code, escaped(reason), conn->address, conn->port);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port);
break;
}
}
- if (conn->purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
+ if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
switch (status_code) {
case 200:
log_info(LD_REND,
@@ -1171,12 +1195,13 @@ connection_dir_client_reached_eof(connection_t *conn)
case 400:
log_warn(LD_REND,"http status 400 (%s) response from dirserver "
"'%s:%d'. Malformed rendezvous descriptor?",
- escaped(reason), conn->address, conn->port);
+ escaped(reason), conn->_base.address, conn->_base.port);
break;
default:
log_warn(LD_REND,"http status %d (%s) response unexpected (server "
"'%s:%d').",
- status_code, escaped(reason), conn->address, conn->port);
+ status_code, escaped(reason), conn->_base.address,
+ conn->_base.port);
break;
}
}
@@ -1186,20 +1211,20 @@ connection_dir_client_reached_eof(connection_t *conn)
/** Called when a directory connection reaches EOF */
int
-connection_dir_reached_eof(connection_t *conn)
+connection_dir_reached_eof(dir_connection_t *conn)
{
int retval;
- if (conn->state != DIR_CONN_STATE_CLIENT_READING) {
+ if (conn->_base.state != DIR_CONN_STATE_CLIENT_READING) {
log_info(LD_HTTP,"conn reached eof, not reading. Closing.");
- connection_close_immediate(conn); /* error: give up on flushing */
- connection_mark_for_close(conn);
+ connection_close_immediate(TO_CONN(conn)); /* error: give up on flushing */
+ connection_mark_for_close(TO_CONN(conn));
return -1;
}
retval = connection_dir_client_reached_eof(conn);
if (retval == 0) /* success */
- conn->state = DIR_CONN_STATE_CLIENT_FINISHED;
- connection_mark_for_close(conn);
+ conn->_base.state = DIR_CONN_STATE_CLIENT_FINISHED;
+ connection_mark_for_close(TO_CONN(conn));
return retval;
}
@@ -1207,10 +1232,10 @@ connection_dir_reached_eof(connection_t *conn)
* directory servers and connections <em>at</em> directory servers.)
*/
int
-connection_dir_process_inbuf(connection_t *conn)
+connection_dir_process_inbuf(dir_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_DIR);
+ tor_assert(conn->_base.type == CONN_TYPE_DIR);
/* Directory clients write, then read data until they receive EOF;
* directory servers read data until they get an HTTP command, then
@@ -1219,9 +1244,9 @@ connection_dir_process_inbuf(connection_t *conn)
*/
/* If we're on the dirserver side, look for a command. */
- if (conn->state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) {
+ if (conn->_base.state == DIR_CONN_STATE_SERVER_COMMAND_WAIT) {
if (directory_handle_command(conn) < 0) {
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return -1;
}
return 0;
@@ -1229,7 +1254,7 @@ connection_dir_process_inbuf(connection_t *conn)
/* XXX for READ states, might want to make sure inbuf isn't too big */
- if (!conn->inbuf_reached_eof)
+ if (!conn->_base.inbuf_reached_eof)
log_debug(LD_HTTP,"Got data, not eof. Leaving on inbuf.");
return 0;
}
@@ -1238,7 +1263,7 @@ connection_dir_process_inbuf(connection_t *conn)
* <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>.
*/
static void
-write_http_status_line(connection_t *conn, int status,
+write_http_status_line(dir_connection_t *conn, int status,
const char *reason_phrase)
{
char buf[256];
@@ -1247,7 +1272,43 @@ write_http_status_line(connection_t *conn, int status,
log_warn(LD_BUG,"Bug: status line too long.");
return;
}
- connection_write_to_buf(buf, strlen(buf), conn);
+ connection_write_to_buf(buf, strlen(buf), TO_CONN(conn));
+}
+
+/** DOCDOC */
+static void
+write_http_response_header(dir_connection_t *conn, ssize_t length,
+ const char *type, const char *encoding)
+{
+ char date[RFC1123_TIME_LEN+1];
+ char tmp[1024];
+ char *cp;
+
+ tor_assert(conn);
+ tor_assert(type);
+
+ format_rfc1123_time(date, time(NULL));
+ cp = tmp;
+ tor_snprintf(cp, sizeof(tmp),
+ "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Type: %s\r\n"
+ X_ADDRESS_HEADER "%s\r\n",
+ date, type, conn->_base.address);
+ cp += strlen(tmp);
+ if (encoding) {
+ tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
+ "Content-Encoding: %s\r\n", encoding);
+ cp += strlen(cp);
+ }
+ if (length >= 0) {
+ tor_snprintf(cp, sizeof(tmp)-(cp-tmp),
+ "Content-Length: %ld\r\n", (long)length);
+ cp += strlen(cp);
+ }
+ if (sizeof(tmp)-(cp-tmp) > 3)
+ memcpy(cp, "\r\n", 3);
+ else
+ tor_assert(0);
+ connection_write_to_buf(tmp, strlen(tmp), TO_CONN(conn));
}
/** Helper function: return 1 if there are any dir conns of purpose
@@ -1267,7 +1328,7 @@ already_fetching_directory(int purpose)
if (conn->type == CONN_TYPE_DIR &&
conn->purpose == purpose &&
!conn->marked_for_close &&
- !router_digest_is_me(conn->identity_digest))
+ !router_digest_is_me(TO_DIR_CONN(conn)->identity_digest))
return 1;
}
return 0;
@@ -1331,7 +1392,8 @@ directory_dump_request_log(void)
static void
note_request(const char *key, size_t bytes)
{
- return;
+ (void)key;
+ (void)bytes;
}
char *
@@ -1347,18 +1409,19 @@ directory_dump_request_log(void)
* conn-\>outbuf. If the request is unrecognized, send a 400.
* Always return 0. */
static int
-directory_handle_command_get(connection_t *conn, char *headers,
+directory_handle_command_get(dir_connection_t *conn, char *headers,
char *body, size_t body_len)
{
size_t dlen;
const char *cp;
char *url = NULL;
- char tmp[8192];
- char date[RFC1123_TIME_LEN+1];
+ /* We ignore the body of a GET request. */
+ (void)body;
+ (void)body_len;
log_debug(LD_DIRSERV,"Received GET command.");
- conn->state = DIR_CONN_STATE_SERVER_WRITING;
+ conn->_base.state = DIR_CONN_STATE_SERVER_WRITING;
if (parse_http_url(headers, &url) < 0) {
write_http_status_line(conn, 400, "Bad request");
@@ -1368,9 +1431,9 @@ directory_handle_command_get(connection_t *conn, char *headers,
if (!strcmp(url,"/tor/") || !strcmp(url,"/tor/dir.z")) { /* dir fetch */
int deflated = !strcmp(url,"/tor/dir.z");
- dlen = dirserv_get_directory(&cp, deflated);
+ cached_dir_t *d = dirserv_get_directory();
- if (dlen == 0) {
+ if (!d) {
log_notice(LD_DIRSERV,"Client asked for the mirrored directory, but we "
"don't have a good one yet. Sending 503 Dir not available.");
write_http_status_line(conn, 503, "Directory unavailable");
@@ -1380,6 +1443,7 @@ directory_handle_command_get(connection_t *conn, char *headers,
tor_free(url);
return 0;
}
+ dlen = deflated ? d->dir_z_len : d->dir_len;
if (global_write_bucket_empty()) {
log_info(LD_DIRSERV,
@@ -1395,16 +1459,18 @@ directory_handle_command_get(connection_t *conn, char *headers,
log_debug(LD_DIRSERV,"Dumping %sdirectory to client.",
deflated?"deflated ":"");
- format_rfc1123_time(date, time(NULL));
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: %s\r\nContent-Encoding: %s\r\n\r\n",
- date,
- (int)dlen,
- deflated?"application/octet-stream":"text/plain",
- deflated?"deflate":"identity");
- connection_write_to_buf(tmp, strlen(tmp), conn);
- connection_write_to_buf(cp, dlen, conn);
+ write_http_response_header(conn, dlen,
+ deflated?"application/octet-stream":"text/plain",
+ deflated?"deflate":"identity");
+ conn->cached_dir = d;
+ conn->cached_dir_offset = 0;
+ if (! deflated)
+ conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD);
+ ++d->refcnt;
+
+ /* Prime the connection with some data. */
+ conn->dir_spool_src = DIR_SPOOL_CACHED_DIR;
+ connection_dirserv_flushed_some(conn);
return 0;
}
@@ -1422,16 +1488,10 @@ directory_handle_command_get(connection_t *conn, char *headers,
return 0;
}
- format_rfc1123_time(date, time(NULL));
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: %s\r\nContent-Encoding: %s\r\n\r\n",
- date,
- (int)dlen,
+ write_http_response_header(conn, dlen,
deflated?"application/octet-stream":"text/plain",
deflated?"deflate":"identity");
- connection_write_to_buf(tmp, strlen(tmp), conn);
- connection_write_to_buf(cp, strlen(cp), conn);
+ connection_write_to_buf(cp, strlen(cp), TO_CONN(conn));
return 0;
}
@@ -1439,12 +1499,12 @@ directory_handle_command_get(connection_t *conn, char *headers,
/* v2 network status fetch. */
size_t url_len = strlen(url);
int deflated = !strcmp(url+url_len-2, ".z");
- smartlist_t *dir_objs = smartlist_create();
+ smartlist_t *dir_fps = smartlist_create();
const char *request_type = NULL;
const char *key = url + strlen("/tor/status/");
if (deflated)
url[url_len-2] = '\0';
- dirserv_get_networkstatus_v2(dir_objs, key);
+ dirserv_get_networkstatus_v2_fingerprints(dir_fps, key);
if (!strcmpstart(key, "fp/"))
request_type = deflated?"/tor/status/fp.z":"/tor/status/fp";
else if (!strcmpstart(key, "authority"))
@@ -1455,32 +1515,24 @@ directory_handle_command_get(connection_t *conn, char *headers,
else
request_type = "/tor/status/?";
tor_free(url);
- if (!smartlist_len(dir_objs)) { /* we failed to create/cache cp */
+ if (!smartlist_len(dir_fps)) { /* we failed to create/cache cp */
write_http_status_line(conn, 503, "Network status object unavailable");
- smartlist_free(dir_objs);
+ smartlist_free(dir_fps);
return 0;
}
- dlen = 0;
- SMARTLIST_FOREACH(dir_objs, cached_dir_t *, d,
- dlen += deflated?d->dir_z_len:d->dir_len);
- note_request(request_type,dlen);
- format_rfc1123_time(date, time(NULL));
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: %s\r\nContent-Encoding: %s\r\n\r\n",
- date,
- (int)dlen,
- deflated?"application/octet-stream":"text/plain",
- deflated?"deflate":"identity");
- connection_write_to_buf(tmp, strlen(tmp), conn);
- SMARTLIST_FOREACH(dir_objs, cached_dir_t *, d,
- {
- if (deflated)
- connection_write_to_buf(d->dir_z, d->dir_z_len, conn);
- else
- connection_write_to_buf(d->dir, d->dir_len, conn);
- });
- smartlist_free(dir_objs);
+ // note_request(request_type,dlen);
+ write_http_response_header(conn, -1,
+ deflated?"application/octet_stream":"text/plain",
+ deflated?"deflate":NULL);
+
+ conn->fingerprint_stack = dir_fps;
+ if (! deflated)
+ conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD);
+
+ /* Prime the connection with some data. */
+ conn->dir_spool_src = DIR_SPOOL_NETWORKSTATUS;
+ connection_dirserv_flushed_some(conn);
+
return 0;
}
@@ -1489,11 +1541,12 @@ directory_handle_command_get(connection_t *conn, char *headers,
int deflated = !strcmp(url+url_len-2, ".z");
int res;
const char *msg;
- smartlist_t *descs = smartlist_create();
const char *request_type = NULL;
if (deflated)
url[url_len-2] = '\0';
- res = dirserv_get_routerdescs(descs, url, &msg);
+ conn->fingerprint_stack = smartlist_create();
+ res = dirserv_get_routerdesc_fingerprints(conn->fingerprint_stack, url,
+ &msg);
if (!strcmpstart(url, "/tor/server/fp/"))
request_type = deflated?"/tor/server/fp.z":"/tor/server/fp";
@@ -1506,62 +1559,22 @@ directory_handle_command_get(connection_t *conn, char *headers,
request_type = deflated?"/tor/server/d.z":"/tor/server/d";
else
request_type = "/tor/server/?";
+ if (!strcmpstart(url, "/tor/server/d/"))
+ conn->dir_spool_src = DIR_SPOOL_SERVER_BY_DIGEST;
+ else
+ conn->dir_spool_src = DIR_SPOOL_SERVER_BY_FP;
tor_free(url);
if (res < 0)
write_http_status_line(conn, 404, msg);
else {
- size_t len = 0;
- format_rfc1123_time(date, time(NULL));
- SMARTLIST_FOREACH(descs, signed_descriptor_t *, ri,
- len += ri->signed_descriptor_len);
- if (deflated) {
- size_t compressed_len;
- char *compressed;
- char *inp = tor_malloc(len+smartlist_len(descs)+1);
- char *cp = inp;
- SMARTLIST_FOREACH(descs, signed_descriptor_t *, ri,
- {
- const char *body = signed_descriptor_get_body(ri);
- memcpy(cp, body, ri->signed_descriptor_len);
- cp += ri->signed_descriptor_len;
- *cp++ = '\n';
- });
- *cp = '\0';
- /* XXXX This could be way more efficiently handled; let's see if it
- * shows up under oprofile. */
- if (tor_gzip_compress(&compressed, &compressed_len,
- inp, cp-inp, ZLIB_METHOD)<0) {
- tor_free(inp);
- smartlist_free(descs);
- return -1;
- }
- tor_free(inp);
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: application/octet-stream\r\n"
- "Content-Encoding: deflate\r\n\r\n",
- date,
- (int)compressed_len);
- note_request(request_type, compressed_len);
- connection_write_to_buf(tmp, strlen(tmp), conn);
- connection_write_to_buf(compressed, compressed_len, conn);
- tor_free(compressed);
- } else {
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: text/plain\r\n\r\n",
- date,
- (int)len);
- note_request(request_type, len);
- connection_write_to_buf(tmp, strlen(tmp), conn);
- SMARTLIST_FOREACH(descs, signed_descriptor_t *, ri,
- {
- const char *body = signed_descriptor_get_body(ri);
- connection_write_to_buf(body, ri->signed_descriptor_len, conn);
- });
- }
+ write_http_response_header(conn, -1,
+ deflated?"application/octet_stream":"text/plain",
+ deflated?"deflate":NULL);
+ if (deflated)
+ conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD);
+ /* Prime the connection with some data. */
+ connection_dirserv_flushed_some(conn);
}
- smartlist_free(descs);
return 0;
}
@@ -1586,16 +1599,11 @@ directory_handle_command_get(connection_t *conn, char *headers,
}
switch (rend_cache_lookup_desc(query, versioned?-1:0, &descp, &desc_len)) {
case 1: /* valid */
- format_rfc1123_time(date, time(NULL));
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: application/octet-stream\r\n\r\n",
- date,
- (int)desc_len);
+ write_http_response_header(conn, desc_len, "application/octet-stream",
+ NULL);
note_request("/tor/rendezvous?/", desc_len);
- connection_write_to_buf(tmp, strlen(tmp), conn);
/* need to send descp separately, because it may include nuls */
- connection_write_to_buf(descp, desc_len, conn);
+ connection_write_to_buf(descp, desc_len, TO_CONN(conn));
break;
case 0: /* well-formed but not present */
write_http_status_line(conn, 404, "Not found");
@@ -1611,14 +1619,8 @@ directory_handle_command_get(connection_t *conn, char *headers,
if (!strcmpstart(url,"/tor/bytes.txt")) {
char *bytes = directory_dump_request_log();
size_t len = strlen(bytes);
- format_rfc1123_time(date, time(NULL));
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: text/plain\r\n\r\n",
- date,
- (int)len);
- connection_write_to_buf(tmp, strlen(tmp), conn);
- connection_write_to_buf(bytes, len, conn);
+ write_http_response_header(conn, len, "text/plain", NULL);
+ connection_write_to_buf(bytes, len, TO_CONN(conn));
tor_free(bytes);
tor_free(url);
return 0;
@@ -1628,14 +1630,32 @@ directory_handle_command_get(connection_t *conn, char *headers,
rewritten to /tor/robots.txt */
char robots[] = "User-agent: *\r\nDisallow: /\r\n";
size_t len = strlen(robots);
- format_rfc1123_time(date, time(NULL));
- tor_snprintf(tmp, sizeof(tmp),
- "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n"
- "Content-Type: text/plain\r\n\r\n",
- date,
- (int)len);
- connection_write_to_buf(tmp, strlen(tmp), conn);
- connection_write_to_buf(robots, len, conn);
+ write_http_response_header(conn, len, "text/plain", NULL);
+ connection_write_to_buf(robots, len, TO_CONN(conn));
+ tor_free(url);
+ return 0;
+ }
+
+ if (!strcmp(url,"/tor/dir-all-weaselhack") &&
+ (conn->_base.addr == 0x7f000001ul) &&
+ authdir_mode(get_options())) {
+ /* XXX until weasel rewrites his scripts XXXX012 */
+ char *new_directory=NULL;
+
+ if (dirserv_dump_directory_to_string(&new_directory,
+ get_identity_key(), 1)) {
+ log_warn(LD_BUG, "Error creating full v1 directory.");
+ tor_free(new_directory);
+ write_http_status_line(conn, 503, "Directory unavailable");
+ return 0;
+ }
+
+ dlen = strlen(new_directory);
+
+ write_http_response_header(conn, dlen, "text/plain", "identity");
+
+ connection_write_to_buf(new_directory, dlen, TO_CONN(conn));
+ tor_free(new_directory);
tor_free(url);
return 0;
}
@@ -1652,16 +1672,14 @@ directory_handle_command_get(connection_t *conn, char *headers,
* response into conn-\>outbuf. If the request is unrecognized, send a
* 400. Always return 0. */
static int
-directory_handle_command_post(connection_t *conn, char *headers,
+directory_handle_command_post(dir_connection_t *conn, char *headers,
char *body, size_t body_len)
{
- const char *cp;
- char *origin = NULL;
char *url = NULL;
log_debug(LD_DIRSERV,"Received POST command.");
- conn->state = DIR_CONN_STATE_SERVER_WRITING;
+ conn->_base.state = DIR_CONN_STATE_SERVER_WRITING;
if (!authdir_mode(get_options())) {
/* we just provide cached directories; we don't want to
@@ -1676,19 +1694,19 @@ directory_handle_command_post(connection_t *conn, char *headers,
return 0;
}
log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
- origin = http_get_origin(headers, conn);
if (!strcmp(url,"/tor/")) { /* server descriptor post */
const char *msg;
int r = dirserv_add_descriptor(body, &msg);
tor_assert(msg);
if (r > 0)
- dirserv_get_directory(&cp, 0); /* rebuild and write to disk */
+ dirserv_get_directory(); /* rebuild and write to disk */
switch (r) {
case -2:
case -1:
case 1:
- log_notice(LD_DIRSERV,"Rejected router descriptor from %s.", origin);
+ log_notice(LD_DIRSERV,"Rejected router descriptor from %s.",
+ conn->_base.address);
/* malformed descriptor, or something wrong */
write_http_status_line(conn, 400, msg);
break;
@@ -1706,7 +1724,7 @@ directory_handle_command_post(connection_t *conn, char *headers,
// char tmp[1024*2+1];
log_fn(LOG_PROTOCOL_WARN, LD_DIRSERV,
"Rejected rend descriptor (length %d) from %s.",
- (int)body_len, origin);
+ (int)body_len, conn->_base.address);
#if 0
if (body_len <= 1024) {
base16_encode(tmp, sizeof(tmp), body, body_len);
@@ -1725,7 +1743,6 @@ directory_handle_command_post(connection_t *conn, char *headers,
done:
tor_free(url);
- tor_free(origin);
return 0;
}
@@ -1735,21 +1752,22 @@ directory_handle_command_post(connection_t *conn, char *headers,
* buffer. Return a 0 on success, or -1 on error.
*/
static int
-directory_handle_command(connection_t *conn)
+directory_handle_command(dir_connection_t *conn)
{
char *headers=NULL, *body=NULL;
size_t body_len=0;
int r;
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_DIR);
+ tor_assert(conn->_base.type == CONN_TYPE_DIR);
- switch (fetch_from_buf_http(conn->inbuf,
+ switch (fetch_from_buf_http(conn->_base.inbuf,
&headers, MAX_HEADERS_SIZE,
&body, &body_len, MAX_BODY_SIZE, 0)) {
case -1: /* overflow */
log_warn(LD_DIRSERV,
- "Invalid input from address '%s'. Closing.", conn->address);
+ "Invalid input from address '%s'. Closing.",
+ conn->_base.address);
return -1;
case 0:
log_debug(LD_DIRSERV,"command not all here yet.");
@@ -1757,6 +1775,7 @@ directory_handle_command(connection_t *conn)
/* case 1, fall through */
}
+ http_set_address_origin(headers, TO_CONN(conn));
//log_debug(LD_DIRSERV,"headers %s, body %s.", headers, body);
if (!strncasecmp(headers,"GET",3))
@@ -1779,23 +1798,24 @@ directory_handle_command(connection_t *conn)
* appropriate.
*/
int
-connection_dir_finished_flushing(connection_t *conn)
+connection_dir_finished_flushing(dir_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_DIR);
+ tor_assert(conn->_base.type == CONN_TYPE_DIR);
- switch (conn->state) {
+ switch (conn->_base.state) {
case DIR_CONN_STATE_CLIENT_SENDING:
log_debug(LD_DIR,"client finished sending command.");
- conn->state = DIR_CONN_STATE_CLIENT_READING;
- connection_stop_writing(conn);
+ conn->_base.state = DIR_CONN_STATE_CLIENT_READING;
+ connection_stop_writing(TO_CONN(conn));
return 0;
case DIR_CONN_STATE_SERVER_WRITING:
log_debug(LD_DIRSERV,"Finished writing server response. Closing.");
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return 0;
default:
- log_warn(LD_BUG,"Bug: called in unexpected state %d.", conn->state);
+ log_warn(LD_BUG,"Bug: called in unexpected state %d.",
+ conn->_base.state);
tor_fragile_assert();
return -1;
}
@@ -1805,16 +1825,16 @@ connection_dir_finished_flushing(connection_t *conn)
/** Connected handler for directory connections: begin sending data to the
* server */
int
-connection_dir_finished_connecting(connection_t *conn)
+connection_dir_finished_connecting(dir_connection_t *conn)
{
tor_assert(conn);
- tor_assert(conn->type == CONN_TYPE_DIR);
- tor_assert(conn->state == DIR_CONN_STATE_CONNECTING);
+ tor_assert(conn->_base.type == CONN_TYPE_DIR);
+ tor_assert(conn->_base.state == DIR_CONN_STATE_CONNECTING);
log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
- conn->address,conn->port);
+ conn->_base.address,conn->_base.port);
- conn->state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
+ conn->_base.state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */
return 0;
}
@@ -1892,21 +1912,21 @@ dir_routerdesc_download_failed(smartlist_t *failed)
* the strings, in order, into <b>fp_out</b>. If <b>compressed_out</b> is
* non-NULL, set it to 1 if the resource ends in ".z", else set it to 0. If
* decode_hex is true, then delete all elements that aren't hex digests, and
- * decode the rest.
+ * decode the rest. If sort_uniq is true, then sort the list and remove
+ * all duplicates.
*/
int
dir_split_resource_into_fingerprints(const char *resource,
smartlist_t *fp_out, int *compressed_out,
- int decode_hex)
+ int decode_hex, int sort_uniq)
{
- int old_len;
+ smartlist_t *fp_tmp = smartlist_create();
tor_assert(fp_out);
- old_len = smartlist_len(fp_out);
- smartlist_split_string(fp_out, resource, "+", 0, 0);
+ smartlist_split_string(fp_tmp, resource, "+", 0, 0);
if (compressed_out)
*compressed_out = 0;
- if (smartlist_len(fp_out) > old_len) {
- char *last = smartlist_get(fp_out,smartlist_len(fp_out)-1);
+ if (smartlist_len(fp_tmp)) {
+ char *last = smartlist_get(fp_tmp,smartlist_len(fp_tmp)-1);
size_t last_len = strlen(last);
if (last_len > 2 && !strcmp(last+last_len-2, ".z")) {
last[last_len-2] = '\0';
@@ -1917,27 +1937,51 @@ dir_split_resource_into_fingerprints(const char *resource,
if (decode_hex) {
int i;
char *cp, *d = NULL;
- for (i = old_len; i < smartlist_len(fp_out); ++i) {
- cp = smartlist_get(fp_out, i);
+ for (i = 0; i < smartlist_len(fp_tmp); ++i) {
+ cp = smartlist_get(fp_tmp, i);
if (strlen(cp) != HEX_DIGEST_LEN) {
log_info(LD_DIR,
"Skipping digest %s with non-standard length.", escaped(cp));
- smartlist_del(fp_out, i--);
+ smartlist_del_keeporder(fp_tmp, i--);
goto again;
}
d = tor_malloc_zero(DIGEST_LEN);
if (base16_decode(d, DIGEST_LEN, cp, HEX_DIGEST_LEN)<0) {
log_info(LD_DIR, "Skipping non-decodable digest %s", escaped(cp));
- smartlist_del(fp_out, i--);
+ smartlist_del_keeporder(fp_tmp, i--);
goto again;
}
- smartlist_set(fp_out, i, d);
+ smartlist_set(fp_tmp, i, d);
d = NULL;
again:
tor_free(cp);
tor_free(d);
}
}
+ if (sort_uniq) {
+ smartlist_t *fp_tmp2 = smartlist_create();
+ int i;
+ if (decode_hex)
+ smartlist_sort_digests(fp_tmp);
+ else
+ smartlist_sort_strings(fp_tmp);
+ if (smartlist_len(fp_tmp))
+ smartlist_add(fp_tmp2, smartlist_get(fp_tmp, 0));
+ for (i = 1; i < smartlist_len(fp_tmp); ++i) {
+ char *cp = smartlist_get(fp_tmp, i);
+ char *last = smartlist_get(fp_tmp2, smartlist_len(fp_tmp2)-1);
+
+ if ((decode_hex && memcmp(cp, last, DIGEST_LEN))
+ || (!decode_hex && strcasecmp(cp, last)))
+ smartlist_add(fp_tmp2, cp);
+ else
+ tor_free(cp);
+ }
+ smartlist_free(fp_tmp);
+ fp_tmp = fp_tmp2;
+ }
+ smartlist_add_all(fp_out, fp_tmp);
+ smartlist_free(fp_tmp);
return 0;
}
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index e85575e6f9..b159afed97 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -36,7 +36,7 @@ static int runningrouters_is_dirty = 1;
static int the_v2_networkstatus_is_dirty = 1;
static void directory_remove_invalid(void);
-static int dirserv_regenerate_directory(void);
+static cached_dir_t *dirserv_regenerate_directory(void);
static char *format_versions_list(config_line_t *ln);
/* Should be static; exposed for testing */
int add_fingerprint_to_dir(const char *nickname, const char *fp,
@@ -51,6 +51,7 @@ dirserv_get_status_impl(const char *fp, const char *nickname,
const char **msg, int should_log);
static int dirserv_thinks_router_is_reachable(routerinfo_t *router,
time_t now);
+static void clear_cached_dir(cached_dir_t *d);
/************** Fingerprint handling code ************/
@@ -121,24 +122,31 @@ dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
return 0;
}
-/** Parse the nickname-\>fingerprint mappings stored in the file named
- * <b>fname</b>. The file format is line-based, with each non-blank
- * holding one nickname, some space, and a fingerprint for that
- * nickname. On success, replace the current fingerprint list with
- * the contents of <b>fname</b> and return 0. On failure, leave the
- * current fingerprint list untouched, and return -1. */
+/** Load the nickname-\>fingerprint mappings stored in the approved-routers
+ * file. The file format is line-based, with each non-blank holding one
+ * nickname, some space, and a fingerprint for that nickname. On success,
+ * replace the current fingerprint list with the new list and return 0. On
+ * failure, leave the current fingerprint list untouched, and
+ * return -1. */
int
-dirserv_parse_fingerprint_file(const char *fname)
+dirserv_load_fingerprint_file(void)
{
+ char fname[512];
char *cf;
char *nickname, *fingerprint;
smartlist_t *fingerprint_list_new;
int result;
config_line_t *front=NULL, *list;
+ or_options_t *options = get_options();
+
+ tor_snprintf(fname, sizeof(fname),
+ "%s/approved-routers", options->DataDirectory);
+ log_info(LD_GENERAL,
+ "Reloading approved fingerprints from \"%s\"...", fname);
cf = read_file_to_str(fname, 0);
if (!cf) {
- if (get_options()->NamingAuthoritativeDir) {
+ if (options->NamingAuthoritativeDir) {
log_warn(LD_FS, "Cannot open fingerprint file '%s'. Failing.", fname);
return -1;
} else {
@@ -454,6 +462,7 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
case FP_INVALID:
ri->is_named = ri->is_valid = 0;
break;
+ case FP_REJECT:
default:
tor_assert(0);
}
@@ -480,7 +489,7 @@ dirserv_add_descriptor(const char *desc, const char **msg)
*msg = NULL;
/* Check: is the descriptor syntactically valid? */
- ri = router_parse_entry_from_string(desc, NULL);
+ ri = router_parse_entry_from_string(desc, NULL, 1);
if (!ri) {
log_warn(LD_DIRSERV, "Couldn't parse uploaded server descriptor");
*msg = "Rejected: Couldn't parse server descriptor.";
@@ -656,7 +665,7 @@ list_single_server_status(routerinfo_t *desc, int is_live)
/** Each server needs to have passed a reachability test no more
* than this number of seconds ago, or he is listed as down in
* the directory. */
-#define REACHABLE_TIMEOUT (30*60)
+#define REACHABLE_TIMEOUT (45*60)
/** Treat a router as alive if
* - It's me, and I'm not hibernating.
@@ -760,11 +769,12 @@ live_enough_for_v1_dir(routerinfo_t *ri, time_t now)
/** Generate a new v1 directory and write it into a newly allocated string.
* Point *<b>dir_out</b> to the allocated string. Sign the
* directory with <b>private_key</b>. Return 0 on success, -1 on
- * failure.
+ * failure. If <b>complete</b> is set, give us all the descriptors;
+ * otherwise leave out non-running and non-valid ones.
*/
int
dirserv_dump_directory_to_string(char **dir_out,
- crypto_pk_env_t *private_key)
+ crypto_pk_env_t *private_key, int complete)
{
char *cp;
char *router_status;
@@ -798,7 +808,7 @@ dirserv_dump_directory_to_string(char **dir_out,
buf_len = 2048+strlen(recommended_versions)+
strlen(router_status);
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
- if (live_enough_for_v1_dir(ri, now))
+ if (complete || live_enough_for_v1_dir(ri, now))
buf_len += ri->cache_info.signed_descriptor_len+1);
buf = tor_malloc(buf_len);
/* We'll be comparing against buf_len throughout the rest of the
@@ -824,7 +834,7 @@ dirserv_dump_directory_to_string(char **dir_out,
{
size_t len = ri->cache_info.signed_descriptor_len;
const char *body;
- if (!live_enough_for_v1_dir(ri, now))
+ if (!complete && !live_enough_for_v1_dir(ri, now))
continue;
if (cp+len+1 >= buf+buf_len)
goto truncated;
@@ -864,12 +874,12 @@ dirserv_dump_directory_to_string(char **dir_out,
}
/** Most recently generated encoded signed directory. (auth dirservers only.)*/
-static cached_dir_t the_directory = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t *the_directory = NULL;
/* Used only by non-auth dirservers: The directory and runningrouters we'll
* serve when requested. */
-static cached_dir_t cached_directory = { NULL, NULL, 0, 0, 0 };
-static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t *cached_directory = NULL;
+static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
/* Used for other dirservers' v2 network statuses. Map from hexdigest to
* cached_dir_t. */
@@ -879,7 +889,7 @@ static digestmap_t *cached_v2_networkstatus = NULL;
* <b>directory</b> published on <b>when</b>, unless <b>when</b> is older than
* the last value, or too far in the future.
*
- * Does not copy <b>directory</b>; free it if it isn't used.
+ * Does not copy <b>directory</b>; frees it if it isn't used.
*/
static void
set_cached_dir(cached_dir_t *d, char *directory, time_t when)
@@ -906,6 +916,32 @@ set_cached_dir(cached_dir_t *d, char *directory, time_t when)
}
}
+/** DOCDOC */
+void
+cached_dir_decref(cached_dir_t *d)
+{
+ if (!d || --d->refcnt > 0)
+ return;
+ clear_cached_dir(d);
+ tor_free(d);
+}
+
+/** DOCDOC */
+static cached_dir_t *
+new_cached_dir(char *s, time_t published)
+{
+ cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t));
+ d->refcnt = 1;
+ d->dir = s;
+ d->dir_len = strlen(s);
+ d->published = published;
+ if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len,
+ ZLIB_METHOD)) {
+ log_warn(LD_BUG, "Error compressing directory");
+ }
+ return d;
+}
+
/** Remove all storage held in <b>d</b>, but do not free <b>d</b> itself. */
static void
clear_cached_dir(cached_dir_t *d)
@@ -917,11 +953,10 @@ clear_cached_dir(cached_dir_t *d)
/** Free all storage held by the cached_dir_t in <b>d</b>. */
static void
-free_cached_dir(void *_d)
+_free_cached_dir(void *_d)
{
cached_dir_t *d = (cached_dir_t *)_d;
- clear_cached_dir(d);
- tor_free(d);
+ cached_dir_decref(d);
}
/** If we have no cached directory, or it is older than <b>when</b>, then
@@ -931,9 +966,12 @@ void
dirserv_set_cached_directory(const char *directory, time_t published,
int is_running_routers)
{
- cached_dir_t *d;
- d = is_running_routers ? &cached_runningrouters : &cached_directory;
- set_cached_dir(d, tor_strdup(directory), published);
+ if (is_running_routers) {
+ set_cached_dir(&cached_runningrouters, tor_strdup(directory), published);
+ } else {
+ cached_dir_decref(cached_directory);
+ cached_directory = new_cached_dir(tor_strdup(directory), published);
+ }
}
/** We've just received a v2 network-status for an authoritative directory
@@ -947,28 +985,30 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
const char *identity,
time_t published)
{
- cached_dir_t *d;
+ cached_dir_t *d, *old_d;
smartlist_t *trusted_dirs;
if (!cached_v2_networkstatus)
cached_v2_networkstatus = digestmap_new();
- if (!(d = digestmap_get(cached_v2_networkstatus, identity))) {
- if (!networkstatus)
- return;
- d = tor_malloc_zero(sizeof(cached_dir_t));
- digestmap_set(cached_v2_networkstatus, identity, d);
- }
+ old_d = digestmap_get(cached_v2_networkstatus, identity);
+ if (!old_d && !networkstatus)
+ return;
- tor_assert(d);
if (networkstatus) {
- if (published > d->published) {
- set_cached_dir(d, tor_strdup(networkstatus), published);
+ if (!old_d || published > old_d->published) {
+ d = new_cached_dir(tor_strdup(networkstatus), published);
+ digestmap_set(cached_v2_networkstatus, identity, d);
+ if (old_d)
+ cached_dir_decref(old_d);
}
} else {
- free_cached_dir(d);
- digestmap_remove(cached_v2_networkstatus, identity);
+ if (old_d) {
+ digestmap_remove(cached_v2_networkstatus, identity);
+ cached_dir_decref(old_d);
+ }
}
+ /* Now purge old entries. */
trusted_dirs = router_get_trusted_dir_servers();
if (digestmap_size(cached_v2_networkstatus) >
smartlist_len(trusted_dirs) + MAX_UNTRUSTED_NETWORKSTATUSES) {
@@ -993,7 +1033,7 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
tor_assert(oldest);
d = digestmap_remove(cached_v2_networkstatus, oldest);
if (d)
- free_cached_dir(d);
+ cached_dir_decref(d);
}
}
@@ -1005,7 +1045,7 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
static cached_dir_t *
dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
cached_dir_t *auth_src,
- time_t dirty, int (*regenerate)(void),
+ time_t dirty, cached_dir_t *(*regenerate)(void),
const char *name,
int is_v1_object)
{
@@ -1018,7 +1058,7 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
/* We're authoritative. */
if (regenerate != NULL) {
if (dirty && dirty + DIR_REGEN_SLACK_TIME < time(NULL)) {
- if (regenerate()) {
+ if (!(auth_src = regenerate())) {
log_err(LD_BUG, "Couldn't generate %s?", name);
exit(1);
}
@@ -1042,10 +1082,11 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
* this kind of object.
**/
static size_t
-dirserv_get_obj(const char **out, int compress,
+dirserv_get_obj(const char **out,
+ int compress,
cached_dir_t *cache_src,
cached_dir_t *auth_src,
- time_t dirty, int (*regenerate)(void),
+ time_t dirty, cached_dir_t *(*regenerate)(void),
const char *name,
int is_v1_object)
{
@@ -1064,53 +1105,54 @@ dirserv_get_obj(const char **out, int compress,
}
}
-/** Set *<b>directory</b> to the most recently generated encoded signed
- * directory, generating a new one as necessary. If not an authoritative
- * directory may return 0 if no directory is yet cached.*/
-size_t
-dirserv_get_directory(const char **directory, int compress)
+/** Return the most recently generated encoded signed directory, generating a
+ * new one as necessary. If not an authoritative directory may return NULL if
+ * no directory is yet cached.*/
+cached_dir_t *
+dirserv_get_directory(void)
{
- return dirserv_get_obj(directory, compress,
- &cached_directory, &the_directory,
- the_directory_is_dirty,
- dirserv_regenerate_directory,
- "server directory", 1);
+ return dirserv_pick_cached_dir_obj(cached_directory, the_directory,
+ the_directory_is_dirty,
+ dirserv_regenerate_directory,
+ "server directory", 1);
}
/**
- * Generate a fresh v1 directory (authdirservers only.)
+ * Generate a fresh v1 directory (authdirservers only); set the_directory
+ * and return a pointer to the new value.
*/
-static int
+static cached_dir_t *
dirserv_regenerate_directory(void)
{
char *new_directory=NULL;
if (dirserv_dump_directory_to_string(&new_directory,
- get_identity_key())) {
+ get_identity_key(), 0)) {
log_warn(LD_BUG, "Error creating directory.");
tor_free(new_directory);
- return -1;
+ return NULL;
}
- set_cached_dir(&the_directory, new_directory, time(NULL));
+ cached_dir_decref(the_directory);
+ the_directory = new_cached_dir(new_directory, time(NULL));
log_info(LD_DIRSERV,"New directory (size %d) has been built.",
- (int)the_directory.dir_len);
+ (int)the_directory->dir_len);
log_debug(LD_DIRSERV,"New directory (size %d):\n%s",
- (int)the_directory.dir_len, the_directory.dir);
+ (int)the_directory->dir_len, the_directory->dir);
the_directory_is_dirty = 0;
/* Save the directory to disk so we re-load it quickly on startup.
*/
- dirserv_set_cached_directory(the_directory.dir, time(NULL), 0);
+ dirserv_set_cached_directory(the_directory->dir, time(NULL), 0);
- return 0;
+ return the_directory;
}
/** For authoritative directories: the current (v1) network status */
-static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
/** Replace the current running-routers list with a newly generated one. */
-static int
+static cached_dir_t *
generate_runningrouters(void)
{
char *s=NULL;
@@ -1155,11 +1197,11 @@ generate_runningrouters(void)
set_cached_dir(&the_runningrouters, s, time(NULL));
runningrouters_is_dirty = 0;
- return 0;
+ return &the_runningrouters;
err:
tor_free(s);
tor_free(router_status);
- return -1;
+ return NULL;
}
/** Set *<b>rr</b> to the most recently generated encoded signed
@@ -1176,7 +1218,7 @@ dirserv_get_runningrouters(const char **rr, int compress)
}
/** For authoritative directories: the current (v2) network status */
-static cached_dir_t the_v2_networkstatus = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t *the_v2_networkstatus = NULL;
static int
should_generate_v2_networkstatus(void)
@@ -1260,6 +1302,9 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
if (smartlist_len(bandwidths)) {
fast_bandwidth = *(uint32_t*)smartlist_get(bandwidths,
smartlist_len(bandwidths)/8);
+ if (fast_bandwidth < ROUTER_REQUIRED_MIN_BANDWIDTH)
+ fast_bandwidth = *(uint32_t*)smartlist_get(bandwidths,
+ smartlist_len(bandwidths)/4);
guard_bandwidth = *(uint32_t*)smartlist_get(bandwidths,
smartlist_len(bandwidths)/2);
}
@@ -1278,7 +1323,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
/** For authoritative directories only: replace the contents of
* <b>the_v2_networkstatus</b> with a newly generated network status
* object. */
-static int
+static cached_dir_t *
generate_v2_networkstatus(void)
{
#define LONGEST_STATUS_FLAG_NAME_LEN 7
@@ -1290,7 +1335,7 @@ generate_v2_networkstatus(void)
/* second line */ \
(LONGEST_STATUS_FLAG_NAME_LEN+1)*N_STATUS_FLAGS + 2)
- int r = -1;
+ cached_dir_t *r = NULL;
size_t len, identity_pkey_len;
char *status = NULL, *client_versions = NULL, *server_versions = NULL,
*identity_pkey = NULL, *hostname = NULL;
@@ -1310,7 +1355,7 @@ generate_v2_networkstatus(void)
int versioning = options->VersioningAuthoritativeDir;
const char *contact;
- if (resolve_my_address(options, &addr, &hostname)<0) {
+ if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) {
log_warn(LD_NET, "Couldn't resolve my hostname");
goto done;
}
@@ -1359,9 +1404,9 @@ generate_v2_networkstatus(void)
naming ? " Names" : "",
versioning ? " Versions" : "",
versioning ? "client-versions " : "",
- client_versions,
+ versioning ? client_versions : "",
versioning ? "\nserver-versions " : "",
- server_versions,
+ versioning ? server_versions : "",
versioning ? "\n" : "",
identity_pkey);
outp = status + strlen(status);
@@ -1452,13 +1497,14 @@ generate_v2_networkstatus(void)
goto done;
}
- set_cached_dir(&the_v2_networkstatus, status, time(NULL));
+ if (the_v2_networkstatus)
+ cached_dir_decref(the_v2_networkstatus);
+ the_v2_networkstatus = new_cached_dir(status, time(NULL));
status = NULL; /* So it doesn't get double-freed. */
the_v2_networkstatus_is_dirty = 0;
- router_set_networkstatus(the_v2_networkstatus.dir, time(NULL), NS_GENERATED,
- NULL);
-
- r = 0;
+ router_set_networkstatus(the_v2_networkstatus->dir,
+ time(NULL), NS_GENERATED, NULL);
+ r = the_v2_networkstatus;
done:
tor_free(client_versions);
tor_free(server_versions);
@@ -1468,6 +1514,45 @@ generate_v2_networkstatus(void)
return r;
}
+/* DOCDOC */
+void
+dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
+ const char *key)
+{
+ tor_assert(result);
+
+ if (!cached_v2_networkstatus)
+ cached_v2_networkstatus = digestmap_new();
+
+ if (should_generate_v2_networkstatus())
+ generate_v2_networkstatus();
+
+ if (!strcmp(key,"authority")) {
+ if (get_options()->AuthoritativeDir) {
+ routerinfo_t *me = router_get_my_routerinfo();
+ if (me)
+ smartlist_add(result,
+ tor_memdup(me->cache_info.identity_digest, DIGEST_LEN));
+ }
+ } else if (!strcmp(key, "all")) {
+ digestmap_iter_t *iter;
+ iter = digestmap_iter_init(cached_v2_networkstatus);
+ while (!digestmap_iter_done(iter)) {
+ const char *ident;
+ void *val;
+ digestmap_iter_get(iter, &ident, &val);
+ smartlist_add(result, tor_memdup(ident, DIGEST_LEN));
+ iter = digestmap_iter_next(cached_v2_networkstatus, iter);
+ }
+ smartlist_sort_digests(result);
+ if (smartlist_len(result) == 0)
+ log_warn(LD_DIRSERV,
+ "Client requested 'all' network status objects; we have none.");
+ } else if (!strcmpstart(key, "fp/")) {
+ dir_split_resource_into_fingerprints(key+3, result, NULL, 1, 1);
+ }
+}
+
/** Look for a network status object as specified by <b>key</b>, which should
* be either "authority" (to find a network status generated by us), a hex
* identity digest (to find a network status generated by given directory), or
@@ -1486,7 +1571,7 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
if (get_options()->AuthoritativeDir) {
cached_dir_t *d =
dirserv_pick_cached_dir_obj(NULL,
- &the_v2_networkstatus,
+ the_v2_networkstatus,
the_v2_networkstatus_is_dirty,
generate_v2_networkstatus,
"network status list", 0);
@@ -1513,7 +1598,7 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
"Client requested 'all' network status objects; we have none.");
} else if (!strcmpstart(key, "fp/")) {
smartlist_t *digests = smartlist_create();
- dir_split_resource_into_fingerprints(key+3, digests, NULL, 1);
+ dir_split_resource_into_fingerprints(key+3, digests, NULL, 1, 1);
SMARTLIST_FOREACH(digests, char *, cp,
{
cached_dir_t *cached;
@@ -1534,6 +1619,44 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
}
}
+/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t
+ * pointers, adds copies of digests to fps_out. For a /tor/server/d/ request,
+ * adds descriptor digests; for other requests, adds identity digests.
+ */
+int
+dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
+ const char **msg)
+{
+ *msg = NULL;
+
+ if (!strcmp(key, "/tor/server/all")) {
+ routerlist_t *rl = router_get_routerlist();
+ SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
+ smartlist_add(fps_out,
+ tor_memdup(r->cache_info.identity_digest, DIGEST_LEN)));
+ } else if (!strcmp(key, "/tor/server/authority")) {
+ routerinfo_t *ri = router_get_my_routerinfo();
+ if (ri)
+ smartlist_add(fps_out,
+ tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN));
+ } else if (!strcmpstart(key, "/tor/server/d/")) {
+ key += strlen("/tor/server/d/");
+ dir_split_resource_into_fingerprints(key, fps_out, NULL, 1, 1);
+ } else if (!strcmpstart(key, "/tor/server/fp/")) {
+ key += strlen("/tor/server/fp/");
+ dir_split_resource_into_fingerprints(key, fps_out, NULL, 1, 1);
+ } else {
+ *msg = "Key not recognized";
+ return -1;
+ }
+
+ if (!smartlist_len(fps_out)) {
+ *msg = "Servers unavailable";
+ return -1;
+ }
+ return 0;
+}
+
/** Add a signed_descriptor_t to <b>descs_out</b> for each router matching
* <b>key</b>. The key should be either
* - "/tor/server/authority" for our own routerinfo;
@@ -1569,7 +1692,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
} else if (!strcmpstart(key, "/tor/server/d/")) {
smartlist_t *digests = smartlist_create();
key += strlen("/tor/server/d/");
- dir_split_resource_into_fingerprints(key, digests, NULL, 1);
+ dir_split_resource_into_fingerprints(key, digests, NULL, 1, 1);
SMARTLIST_FOREACH(digests, const char *, d,
{
signed_descriptor_t *sd = router_get_by_descriptor_digest(d);
@@ -1582,7 +1705,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
smartlist_t *digests = smartlist_create();
time_t cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH;
key += strlen("/tor/server/fp/");
- dir_split_resource_into_fingerprints(key, digests, NULL, 1);
+ dir_split_resource_into_fingerprints(key, digests, NULL, 1, 1);
SMARTLIST_FOREACH(digests, const char *, d,
{
if (router_digest_is_me(d)) {
@@ -1633,6 +1756,7 @@ dirserv_orconn_tls_done(const char *address,
tor_assert(address);
tor_assert(digest_rcvd);
tor_assert(nickname_rcvd);
+ (void) as_advertised; // XXXX This should really be implemented. -NM
// XXXXNM We should really have a better solution here than dropping
// XXXXNM whole routers; otherwise, they come back way too easily.
@@ -1671,6 +1795,211 @@ dirserv_orconn_tls_done(const char *address,
}
}
+/** Auth dir server only: if <b>try_all</b> is 1, launch connections to
+ * all known routers; else we want to load balance such that we only
+ * try a few connections per call.
+ *
+ * The load balancing is such that if we get called once every ten
+ * seconds, we will cycle through all the tests in 1280 seconds (a
+ * bit over 20 minutes).
+ */
+void
+dirserv_test_reachability(int try_all)
+{
+ time_t now = time(NULL);
+ routerlist_t *rl = router_get_routerlist();
+ static char ctr = 0;
+
+ SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, {
+ const char *id_digest = router->cache_info.identity_digest;
+ if (router_is_me(router))
+ continue;
+ if (try_all || (((uint8_t)id_digest[0]) % 128) == ctr) {
+ log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
+ router->nickname, router->address, router->or_port);
+ /* Remember when we started trying to determine reachability */
+ if (!router->testing_since)
+ router->testing_since = now;
+ connection_or_connect(router->addr, router->or_port,
+ id_digest);
+ }
+ });
+ if (!try_all) /* increment ctr */
+ ctr = (ctr + 1) % 128;
+}
+
+/** When we're spooling data onto our outbuf, add more whenever we dip
+ * below this threshold. */
+#define DIRSERV_BUFFER_MIN 16384
+
+static int
+connection_dirserv_finish_spooling(dir_connection_t *conn)
+{
+ if (conn->zlib_state) {
+ connection_write_to_buf_zlib(conn, "", 0, 1);
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ conn->dir_spool_src = DIR_SPOOL_NONE;
+ return 0;
+}
+
+/** DOCDOC */
+static int
+connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
+{
+ int by_fp = conn->dir_spool_src == DIR_SPOOL_SERVER_BY_FP;
+
+ while (smartlist_len(conn->fingerprint_stack) &&
+ buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ const char *body;
+ char *fp = smartlist_pop_last(conn->fingerprint_stack);
+ signed_descriptor_t *sd = NULL;
+ if (by_fp) {
+ if (router_digest_is_me(fp)) {
+ sd = &(router_get_my_routerinfo()->cache_info);
+ } else {
+ routerinfo_t *ri = router_get_by_digest(fp);
+ if (ri &&
+ ri->cache_info.published_on > time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH)
+ sd = &ri->cache_info;
+ }
+ } else
+ sd = router_get_by_descriptor_digest(fp);
+ tor_free(fp);
+ if (!sd)
+ continue;
+ body = signed_descriptor_get_body(sd);
+ if (conn->zlib_state) {
+ int last = ! smartlist_len(conn->fingerprint_stack);
+ connection_write_to_buf_zlib(conn, body,
+ sd->signed_descriptor_len, last);
+ if (last) {
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ } else {
+ connection_write_to_buf(body,
+ sd->signed_descriptor_len,
+ TO_CONN(conn));
+ }
+ }
+
+ if (!smartlist_len(conn->fingerprint_stack)) {
+ /* We just wrote the last one; finish up. */
+ conn->dir_spool_src = DIR_SPOOL_NONE;
+ smartlist_free(conn->fingerprint_stack);
+ conn->fingerprint_stack = NULL;
+ }
+ return 0;
+}
+
+/** DOCDOC */
+static int
+connection_dirserv_add_dir_bytes_to_outbuf(dir_connection_t *conn)
+{
+ int bytes, remaining;
+
+ bytes = DIRSERV_BUFFER_MIN - buf_datalen(conn->_base.outbuf);
+ tor_assert(bytes > 0);
+ tor_assert(conn->cached_dir);
+ if (bytes < 8192)
+ bytes = 8192;
+ remaining = conn->cached_dir->dir_z_len - conn->cached_dir_offset;
+ if (bytes > remaining)
+ bytes = remaining;
+
+ if (conn->zlib_state) {
+ connection_write_to_buf_zlib(conn,
+ conn->cached_dir->dir_z + conn->cached_dir_offset,
+ bytes, bytes == remaining);
+ } else {
+ connection_write_to_buf(conn->cached_dir->dir_z + conn->cached_dir_offset,
+ bytes, TO_CONN(conn));
+ }
+ conn->cached_dir_offset += bytes;
+ if (conn->cached_dir_offset == (int)conn->cached_dir->dir_z_len) {
+ /* We just wrote the last one; finish up. */
+ connection_dirserv_finish_spooling(conn);
+ cached_dir_decref(conn->cached_dir);
+ conn->cached_dir = NULL;
+ }
+ return 0;
+}
+
+/* DOCDOC */
+static int
+connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn)
+{
+
+ while (buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ if (conn->cached_dir) {
+ int uncompressing = (conn->zlib_state != NULL);
+ int r = connection_dirserv_add_dir_bytes_to_outbuf(conn);
+ if (conn->dir_spool_src == DIR_SPOOL_NONE) {
+ /* add_dir_bytes thinks we're done with the cached_dir. But we
+ * may have more cached_dirs! */
+ conn->dir_spool_src = DIR_SPOOL_NETWORKSTATUS;
+ /* This bit is tricky. If we were uncompressing the last
+ * networkstatus, we may need to make a new zlib object to
+ * uncompress the next one. */
+ if (uncompressing && ! conn->zlib_state &&
+ conn->fingerprint_stack &&
+ smartlist_len(conn->fingerprint_stack)) {
+ conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD);
+ }
+ }
+ if (r) return r;
+ } else if (conn->fingerprint_stack &&
+ smartlist_len(conn->fingerprint_stack)) {
+ /* Add another networkstatus; start serving it. */
+ char *fp = smartlist_pop_last(conn->fingerprint_stack);
+ cached_dir_t *d;
+ if (router_digest_is_me(fp))
+ d = the_v2_networkstatus;
+ else
+ d = digestmap_get(cached_v2_networkstatus, fp);
+ tor_free(fp);
+ if (d) {
+ ++d->refcnt;
+ conn->cached_dir = d;
+ conn->cached_dir_offset = 0;
+ }
+ } else {
+ connection_dirserv_finish_spooling(conn);
+ if (conn->fingerprint_stack)
+ smartlist_free(conn->fingerprint_stack);
+ conn->fingerprint_stack = NULL;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/** Called whenever we have flushed some directory data in state
+ * SERVER_WRITING. */
+int
+connection_dirserv_flushed_some(dir_connection_t *conn)
+{
+ tor_assert(conn->_base.state == DIR_CONN_STATE_SERVER_WRITING);
+
+ if (buf_datalen(conn->_base.outbuf) >= DIRSERV_BUFFER_MIN)
+ return 0;
+
+ switch (conn->dir_spool_src) {
+ case DIR_SPOOL_SERVER_BY_DIGEST:
+ case DIR_SPOOL_SERVER_BY_FP:
+ return connection_dirserv_add_servers_to_outbuf(conn);
+ case DIR_SPOOL_CACHED_DIR:
+ return connection_dirserv_add_dir_bytes_to_outbuf(conn);
+ case DIR_SPOOL_NETWORKSTATUS:
+ return connection_dirserv_add_networkstatus_bytes_to_outbuf(conn);
+ case DIR_SPOOL_NONE:
+ default:
+ return 0;
+ }
+}
+
/** Release all storage used by the directory server. */
void
dirserv_free_all(void)
@@ -1683,13 +2012,13 @@ dirserv_free_all(void)
smartlist_free(fingerprint_list);
fingerprint_list = NULL;
}
- clear_cached_dir(&the_directory);
+ cached_dir_decref(the_directory);
clear_cached_dir(&the_runningrouters);
- clear_cached_dir(&the_v2_networkstatus);
- clear_cached_dir(&cached_directory);
+ cached_dir_decref(the_v2_networkstatus);
+ cached_dir_decref(cached_directory);
clear_cached_dir(&cached_runningrouters);
if (cached_v2_networkstatus) {
- digestmap_free(cached_v2_networkstatus, free_cached_dir);
+ digestmap_free(cached_v2_networkstatus, _free_cached_dir);
cached_v2_networkstatus = NULL;
}
}
diff --git a/src/or/dns.c b/src/or/dns.c
index 5303ced78a..adfadb6f13 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -7,20 +7,21 @@ const char dns_c_id[] =
/**
* \file dns.c
- * \brief Implements a farm of 'DNS worker' threads or processes to
- * perform DNS lookups for onion routers and cache the results.
- * [This needs to be done in the background because of the lack of a
- * good, ubiquitous asynchronous DNS implementation.]
+ * \brief Implements a local cache for DNS results for Tor servers.
+ * We provide two asynchronous backend implementations:
+ * 1) A farm of 'DNS worker' threads or processes to perform DNS lookups for
+ * onion routers and cache the results.
+ * 2) A wrapper around Adam Langley's eventdns.c code, to send requests
+ * to the nameservers asynchronously.
+ * (We can't just use gethostbyname() and friends because we really need to
+ * be nonblocking.)
**/
-/* See
- * http://elvin.dstc.com/ListArchive/elvin-dev/archive/2001/09/msg00027.html
- * for some approaches to asynchronous dns. We will want to switch once one of
- * them becomes more commonly available.
- */
-
#include "or.h"
#include "../common/ht.h"
+#ifdef USE_EVENTDNS
+#include "eventdns.h"
+#endif
/** Longest hostname we're willing to resolve. */
#define MAX_ADDRESSLEN 256
@@ -33,51 +34,103 @@ const char dns_c_id[] =
/** If more than this many processes are idle, shut down the extras. */
#define MAX_IDLE_DNSWORKERS 10
+/** How long will we wait for an answer from the resolver before we decide
+ * that the resolver is wedged? */
+#define RESOLVE_MAX_TIMEOUT 300
+
/** Possible outcomes from hostname lookup: permanent failure,
* transient (retryable) failure, and success. */
#define DNS_RESOLVE_FAILED_TRANSIENT 1
#define DNS_RESOLVE_FAILED_PERMANENT 2
#define DNS_RESOLVE_SUCCEEDED 3
+#ifndef USE_EVENTDNS
/** How many dnsworkers we have running right now. */
static int num_dnsworkers=0;
/** How many of the running dnsworkers have an assigned task right now. */
static int num_dnsworkers_busy=0;
/** When did we last rotate the dnsworkers? */
static time_t last_rotation_time=0;
+#else
+/** Have we currently configured nameservers with eventdns? */
+static int nameservers_configured = 0;
+/** What was the resolv_conf fname we last used when configuring the
+ * nameservers? Used to check whether we need to reconfigure. */
+static char *resolv_conf_fname = NULL;
+/** What was the mtime on the resolv.conf file we last used when configuring
+ * the nameservers? Used to check whether we need to reconfigure. */
+static time_t resolv_conf_mtime = 0;
+#endif
/** Linked list of connections waiting for a DNS answer. */
typedef struct pending_connection_t {
- connection_t *conn;
+ edge_connection_t *conn;
struct pending_connection_t *next;
} pending_connection_t;
+#define CACHED_RESOLVE_MAGIC 0x1234F00D
+
+/* Possible states for a cached resolve_t */
+/** We are waiting for the resolver system to tell us an answer here.
+ * When we get one, or when we time out, the state of this cached_resolve_t
+ * will become "DONE" and we'll possibly add a CACHED_VALID or a CACHED_FAILED
+ * entry. This cached_resolve_t will be in the hash table so that we will
+ * know not to launch more requests for this addr, but rather to add more
+ * connections to the pending list for the addr. */
+#define CACHE_STATE_PENDING 0
+/** This used to be a pending cached_resolve_t, and we got an answer for it.
+ * Now we're waiting for this cached_resolve_t to expire. This should
+ * have no pending connections, and should not appear in the hash table. */
+#define CACHE_STATE_DONE 1
+/** We are caching an answer for this address. This should have no pending
+ * connections, and should appear in the hash table. */
+#define CACHE_STATE_CACHED_VALID 2
+/** We are caching a failure for this address. This should have no pending
+ * connections, and should appear in the hash table */
+#define CACHE_STATE_CACHED_FAILED 3
+
/** A DNS request: possibly completed, possibly pending; cached_resolve
* structs are stored at the OR side in a hash table, and as a linked
* list from oldest to newest.
*/
typedef struct cached_resolve_t {
HT_ENTRY(cached_resolve_t) node;
+ uint32_t magic;
char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
- uint32_t addr; /**< IPv4 addr for <b>address</b>. */
- char state; /**< 0 is pending; 1 means answer is valid; 2 means resolve
- * failed. */
-#define CACHE_STATE_PENDING 0
-#define CACHE_STATE_VALID 1
-#define CACHE_STATE_FAILED 2
- uint32_t expire; /**< Remove items from cache after this time. */
+ union {
+ uint32_t addr; /**< IPv4 addr for <b>address</b>. */
+ char *hostname; /**< Hostname for <b>address</b> (if a reverse lookup) */
+ } result;
+ uint8_t state; /**< Is this cached entry pending/done/valid/failed? */
+ uint8_t is_reverse; /**< Is this a reverse (addr-to-hostname) lookup? */
+ time_t expire; /**< Remove items from cache after this time. */
+ uint32_t ttl; /**< What TTL did the nameserver tell us? */
+ /** Connections that want to know when we get an answer for this resolve. */
pending_connection_t *pending_connections;
- struct cached_resolve_t *next;
} cached_resolve_t;
-static void purge_expired_resolves(uint32_t now);
-static int assign_to_dnsworker(connection_t *exitconn);
-static void dns_purge_resolve(cached_resolve_t *resolve);
-static void dns_found_answer(char *address, uint32_t addr, char outcome);
-static int dnsworker_main(void *data);
+static void purge_expired_resolves(time_t now);
+static void dns_found_answer(const char *address, int is_reverse,
+ uint32_t addr, const char *hostname, char outcome,
+ uint32_t ttl);
+static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type);
+static int launch_resolve(edge_connection_t *exitconn);
+#ifndef USE_EVENTDNS
+static void dnsworkers_rotate(void);
+static void dnsworker_main(void *data);
static int spawn_dnsworker(void);
static int spawn_enough_dnsworkers(void);
-static void send_resolved_cell(connection_t *conn, uint8_t answer_type);
+#else
+static int configure_nameservers(int force);
+static int answer_is_wildcarded(const char *ip);
+#endif
+#ifdef DEBUG_DNS_CACHE
+static void _assert_cache_ok(void);
+#define assert_cache_ok() _assert_cache_ok()
+#else
+#define assert_cache_ok() do {} while (0)
+#endif
+static void assert_resolve_ok(cached_resolve_t *resolve);
/** Hash table of cached_resolve objects. */
static HT_HEAD(cache_map, cached_resolve_t) cache_root;
@@ -88,9 +141,11 @@ static INLINE int
cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b)
{
/* make this smarter one day? */
+ assert_resolve_ok(a); // Not b; b may be just a search.
return !strncmp(a->address, b->address, MAX_ADDRESSLEN);
}
+/** Hash function for cached_resolve objects */
static INLINE unsigned int
cached_resolve_hash(cached_resolve_t *a)
{
@@ -109,12 +164,82 @@ init_cache_map(void)
HT_INIT(&cache_root);
}
+#ifdef USE_EVENTDNS
+/** Helper: called by eventdns when eventdns wants to log something. */
+static void
+eventdns_log_cb(int warn, const char *msg)
+{
+ if (!strcmpstart(msg, "Resolve requested for") &&
+ get_options()->SafeLogging) {
+ log(LOG_INFO, LD_EXIT, "eventdns: Resolve requested.");
+ return;
+ } else if (!strcmpstart(msg, "Search: ")) {
+ return;
+ }
+ log(warn?LOG_WARN:LOG_INFO, LD_EXIT, "eventdns: %s", msg);
+}
+#endif
+
/** Initialize the DNS subsystem; called by the OR process. */
-void
+int
dns_init(void)
{
init_cache_map();
+#ifdef USE_EVENTDNS
+ if (server_mode(get_options()))
+ return configure_nameservers(1);
+#else
+ dnsworkers_rotate();
+#endif
+ return 0;
+}
+
+/** Called when DNS-related options change (or may have changed) */
+void
+dns_reset(void)
+{
+#ifdef USE_EVENTDNS
+ or_options_t *options = get_options();
+ if (! server_mode(options)) {
+ eventdns_clear_nameservers_and_suspend();
+ eventdns_search_clear();
+ nameservers_configured = 0;
+ tor_free(resolv_conf_fname);
+ resolv_conf_mtime = 0;
+ } else {
+ if (configure_nameservers(0) < 0)
+ /* XXXX */
+ return;
+ }
+#else
dnsworkers_rotate();
+#endif
+}
+
+/** Helper: Given a TTL from a DNS response, determine what TTL to give the
+ * OP that asked us to resolve it. */
+uint32_t
+dns_clip_ttl(uint32_t ttl)
+{
+ if (ttl < MIN_DNS_TTL)
+ return MIN_DNS_TTL;
+ else if (ttl > MAX_DNS_TTL)
+ return MAX_DNS_TTL;
+ else
+ return ttl;
+}
+
+/** Helper: Given a TTL from a DNS response, determine how long to hold it in
+ * our cache. */
+static uint32_t
+dns_get_expiry_ttl(uint32_t ttl)
+{
+ if (ttl < MIN_DNS_TTL)
+ return MIN_DNS_TTL;
+ else if (ttl > MAX_DNS_ENTRY_AGE)
+ return MAX_DNS_ENTRY_AGE;
+ else
+ return ttl;
}
/** Helper: free storage held by an entry in the DNS cache. */
@@ -126,10 +251,41 @@ _free_cached_resolve(cached_resolve_t *r)
r->pending_connections = victim->next;
tor_free(victim);
}
+ if (r->is_reverse)
+ tor_free(r->result.hostname);
+ r->magic = 0xFF00FF00;
tor_free(r);
}
-/** Free all storage held in the DNS cache */
+/** Compare two cached_resolve_t pointers by expiry time, and return
+ * less-than-zero, zero, or greater-than-zero as appropriate. Used for
+ * the priority queue implementation. */
+static int
+_compare_cached_resolves_by_expiry(const void *_a, const void *_b)
+{
+ const cached_resolve_t *a = _a, *b = _b;
+ return a->expire - b->expire;
+}
+
+/** Priority queue of cached_resolve_t objects to let us know when they
+ * will expire. */
+static smartlist_t *cached_resolve_pqueue = NULL;
+
+/** Set an expiry time for a cached_resolve_t, and add it to the expiry
+ * priority queue */
+static void
+set_expiry(cached_resolve_t *resolve, time_t expires)
+{
+ tor_assert(resolve && resolve->expire == 0);
+ if (!cached_resolve_pqueue)
+ cached_resolve_pqueue = smartlist_create();
+ resolve->expire = expires;
+ smartlist_pqueue_add(cached_resolve_pqueue,
+ _compare_cached_resolves_by_expiry,
+ resolve);
+}
+
+/** Free all storage held in the DNS cache and related structures. */
void
dns_free_all(void)
{
@@ -140,78 +296,112 @@ dns_free_all(void)
_free_cached_resolve(item);
}
HT_CLEAR(cache_map, &cache_root);
+ if (cached_resolve_pqueue)
+ smartlist_free(cached_resolve_pqueue);
+ cached_resolve_pqueue = NULL;
+#ifdef USE_EVENTDNS
+ tor_free(resolv_conf_fname);
+#endif
}
-/** Linked list of resolved addresses, oldest to newest. */
-static cached_resolve_t *oldest_cached_resolve = NULL;
-static cached_resolve_t *newest_cached_resolve = NULL;
-
/** Remove every cached_resolve whose <b>expire</b> time is before <b>now</b>
* from the cache. */
static void
-purge_expired_resolves(uint32_t now)
+purge_expired_resolves(time_t now)
{
- cached_resolve_t *resolve;
+ cached_resolve_t *resolve, *removed;
pending_connection_t *pend;
- connection_t *pendconn;
+ edge_connection_t *pendconn;
+
+ assert_cache_ok();
+ if (!cached_resolve_pqueue)
+ return;
+
+ while (smartlist_len(cached_resolve_pqueue)) {
+ resolve = smartlist_get(cached_resolve_pqueue, 0);
+ if (resolve->expire > now)
+ break;
+ smartlist_pqueue_pop(cached_resolve_pqueue,
+ _compare_cached_resolves_by_expiry);
- /* this is fast because the linked list
- * oldest_cached_resolve is ordered by when they came in.
- */
- while (oldest_cached_resolve && (oldest_cached_resolve->expire < now)) {
- resolve = oldest_cached_resolve;
- log_debug(LD_EXIT,
- "Forgetting old cached resolve (address %s, expires %lu)",
- escaped_safe_str(resolve->address),
- (unsigned long)resolve->expire);
if (resolve->state == CACHE_STATE_PENDING) {
log_debug(LD_EXIT,
- "Bug: Expiring a dns resolve %s that's still pending."
- " Forgot to cull it?", escaped_safe_str(resolve->address));
- tor_fragile_assert();
+ "Expiring a dns resolve %s that's still pending. Forgot to "
+ "cull it? DNS resolve didn't tell us about the timeout?",
+ escaped_safe_str(resolve->address));
+ } else if (resolve->state == CACHE_STATE_CACHED_VALID ||
+ resolve->state == CACHE_STATE_CACHED_FAILED) {
+ log_debug(LD_EXIT,
+ "Forgetting old cached resolve (address %s, expires %lu)",
+ escaped_safe_str(resolve->address),
+ (unsigned long)resolve->expire);
+ tor_assert(!resolve->pending_connections);
+ } else {
+ tor_assert(resolve->state == CACHE_STATE_DONE);
+ tor_assert(!resolve->pending_connections);
}
+
if (resolve->pending_connections) {
log_debug(LD_EXIT,
- "Closing pending connections on expiring DNS resolve!");
+ "Closing pending connections on timed-out DNS resolve!");
tor_fragile_assert();
while (resolve->pending_connections) {
pend = resolve->pending_connections;
resolve->pending_connections = pend->next;
/* Connections should only be pending if they have no socket. */
- tor_assert(pend->conn->s == -1);
+ tor_assert(pend->conn->_base.s == -1);
pendconn = pend->conn;
connection_edge_end(pendconn, END_STREAM_REASON_TIMEOUT,
pendconn->cpath_layer);
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
- connection_free(pendconn);
+ connection_free(TO_CONN(pendconn));
tor_free(pend);
}
}
- oldest_cached_resolve = resolve->next;
- if (!oldest_cached_resolve) /* if there are no more, */
- newest_cached_resolve = NULL; /* then make sure the list's tail knows
- * that too */
- HT_REMOVE(cache_map, &cache_root, resolve);
- tor_free(resolve);
+
+ if (resolve->state == CACHE_STATE_CACHED_VALID ||
+ resolve->state == CACHE_STATE_CACHED_FAILED ||
+ resolve->state == CACHE_STATE_PENDING) {
+ removed = HT_REMOVE(cache_map, &cache_root, resolve);
+ if (removed != resolve) {
+ log_err(LD_BUG, "The expired resolve we purged didn't match any in"
+ " the cache. Tried to purge %s (%p); instead got %s (%p).",
+ resolve->address, (void*)resolve,
+ removed ? removed->address : "NULL", (void*)remove);
+ }
+ tor_assert(removed == resolve);
+ if (resolve->is_reverse)
+ tor_free(resolve->result.hostname);
+ resolve->magic = 0xF0BBF0BB;
+ tor_free(resolve);
+ } else {
+ /* This should be in state DONE. Make sure it's not in the cache. */
+ cached_resolve_t *tmp = HT_FIND(cache_map, &cache_root, resolve);
+ tor_assert(tmp != resolve);
+ }
}
+
+ assert_cache_ok();
}
-/** Send a response to the RESOVLE request of a connection. answer_type must
+/** Send a response to the RESOLVE request of a connection. answer_type must
* be one of RESOLVED_TYPE_(IPV4|ERROR|ERROR_TRANSIENT) */
static void
-send_resolved_cell(connection_t *conn, uint8_t answer_type)
+send_resolved_cell(edge_connection_t *conn, uint8_t answer_type)
{
char buf[RELAY_PAYLOAD_SIZE];
size_t buflen;
+ uint32_t ttl;
buf[0] = answer_type;
+ ttl = dns_clip_ttl(conn->address_ttl);
switch (answer_type)
{
case RESOLVED_TYPE_IPV4:
buf[1] = 4;
- set_uint32(buf+2, htonl(conn->addr));
- set_uint32(buf+6, htonl(MAX_DNS_ENTRY_AGE)); /*XXXX send a real TTL*/
+ set_uint32(buf+2, htonl(conn->_base.addr));
+ set_uint32(buf+6, htonl(ttl));
buflen = 10;
break;
case RESOLVED_TYPE_ERROR_TRANSIENT:
@@ -219,35 +409,91 @@ send_resolved_cell(connection_t *conn, uint8_t answer_type)
{
const char *errmsg = "Error resolving hostname";
int msglen = strlen(errmsg);
- int ttl = (answer_type == RESOLVED_TYPE_ERROR ? MAX_DNS_ENTRY_AGE : 0);
+
buf[1] = msglen;
strlcpy(buf+2, errmsg, sizeof(buf)-2);
- set_uint32(buf+2+msglen, htonl((uint32_t)ttl));
+ set_uint32(buf+2+msglen, htonl(ttl));
buflen = 6+msglen;
break;
}
default:
tor_assert(0);
+ return;
}
+ // log_notice(LD_EXIT, "Sending a regular RESOLVED reply: ");
connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
RELAY_COMMAND_RESOLVED, buf, buflen,
conn->cpath_layer);
}
-/** Link <b>r</b> into the hash table of address-to-result mappings, and add it
- * to the linked list of resolves-by-age. */
+/** Send a response to the RESOLVE request of a connection for an in-addr.arpa
+ * address on connection <b>conn</b> which yielded the result <b>hostname</b>.
+ * The answer type will be RESOLVED_HOSTNAME.
+ */
static void
-insert_resolve(cached_resolve_t *r)
+send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname)
{
- /* add us to the linked list of resolves */
- if (!oldest_cached_resolve) {
- oldest_cached_resolve = r;
- } else {
- newest_cached_resolve->next = r;
+ char buf[RELAY_PAYLOAD_SIZE];
+ size_t buflen;
+ uint32_t ttl;
+ size_t namelen = strlen(hostname);
+
+ tor_assert(namelen < 256);
+ ttl = dns_clip_ttl(conn->address_ttl);
+
+ buf[0] = RESOLVED_TYPE_HOSTNAME;
+ buf[1] = (uint8_t)namelen;
+ memcpy(buf+2, hostname, namelen);
+ set_uint32(buf+2+namelen, htonl(ttl));
+ buflen = 2+namelen+4;
+
+ // log_notice(LD_EXIT, "Sending a reply RESOLVED reply: %s", hostname);
+ connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
+ RELAY_COMMAND_RESOLVED, buf, buflen,
+ conn->cpath_layer);
+ // log_notice(LD_EXIT, "Sent");
+}
+
+/** Given a lower-case <b>address</b>, check to see whether it's a
+ * 1.2.3.4.in-addr.arpa address used for reverse lookups. If so,
+ * parse it and place the address in <b>in</b> if present. Return 1 on success;
+ * 0 if the address is not in in-addr.arpa format, and -1 if the address is
+ * malformed. */
+static int
+parse_inaddr_arpa_address(const char *address, struct in_addr *in)
+{
+ char buf[INET_NTOA_BUF_LEN];
+ char *cp;
+ size_t len;
+ struct in_addr inaddr;
+
+ cp = strstr(address, ".in-addr.arpa");
+ if (!cp || *(cp+strlen(".in-addr.arpa")))
+ return 0; /* not an .in-addr.arpa address */
+
+ len = cp - address;
+
+ if (len >= INET_NTOA_BUF_LEN)
+ return -1; /* Too long. */
+
+ memcpy(buf, address, len);
+ buf[len] = '\0';
+ if (tor_inet_aton(buf, &inaddr) == 0)
+ return -1; /* malformed. */
+
+ if (in) {
+ uint32_t a;
+ /* reverse the bytes */
+ a = ( ((inaddr.s_addr & 0x000000fful) << 24)
+ |((inaddr.s_addr & 0x0000ff00ul) << 8)
+ |((inaddr.s_addr & 0x00ff0000ul) >> 8)
+ |((inaddr.s_addr & 0xff000000ul) >> 24));
+ inaddr.s_addr = a;
+
+ memcpy(in, &inaddr, sizeof(inaddr));
}
- newest_cached_resolve = r;
- HT_INSERT(cache_map, &cache_root, r);
+ return 1;
}
/** See if we have a cache entry for <b>exitconn</b>-\>address. if so,
@@ -261,22 +507,28 @@ insert_resolve(cached_resolve_t *r)
* dns farm, and return 0.
*/
int
-dns_resolve(connection_t *exitconn)
+dns_resolve(edge_connection_t *exitconn)
{
cached_resolve_t *resolve;
cached_resolve_t search;
pending_connection_t *pending_connection;
- struct in_addr in;
circuit_t *circ;
- uint32_t now = time(NULL);
- assert_connection_ok(exitconn, 0);
- tor_assert(exitconn->s == -1);
+ struct in_addr in;
+ time_t now = time(NULL);
+ int is_reverse = 0, is_resolve, r;
+ assert_connection_ok(TO_CONN(exitconn), 0);
+ tor_assert(exitconn->_base.s == -1);
- /* first check if exitconn->address is an IP. If so, we already
+ assert_cache_ok();
+
+ is_resolve = exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE;
+
+ /* first check if exitconn->_base.address is an IP. If so, we already
* know the answer. */
- if (tor_inet_aton(exitconn->address, &in) != 0) {
- exitconn->addr = ntohl(in.s_addr);
- if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
+ if (tor_inet_aton(exitconn->_base.address, &in) != 0) {
+ exitconn->_base.addr = ntohl(in.s_addr);
+ exitconn->address_ttl = DEFAULT_DNS_TTL;
+ if (is_resolve)
send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
return 1;
}
@@ -285,13 +537,51 @@ dns_resolve(connection_t *exitconn)
* resolves in the hash table. */
purge_expired_resolves(now);
- /* lower-case exitconn->address, so it's in canonical form */
- tor_strlower(exitconn->address);
+ /* lower-case exitconn->_base.address, so it's in canonical form */
+ tor_strlower(exitconn->_base.address);
+
+ /* Check whether this is a reverse lookup. If it's malformed, or it's a
+ * .in-addr.arpa address but this isn't a resolve request, kill the
+ * connecction.
+ */
+ if ((r = parse_inaddr_arpa_address(exitconn->_base.address, NULL)) != 0) {
+ if (r == 1)
+ is_reverse = 1;
+
+#ifdef USE_EVENTDNS
+ if (!is_reverse || !is_resolve) {
+ if (!is_reverse)
+ log_info(LD_EXIT, "Bad .in-addr.arpa address \"%s\"; sending error.",
+ escaped_safe_str(exitconn->_base.address));
+ else if (!is_resolve)
+ log_info(LD_EXIT,
+ "Attempt to connect to a .in-addr.arpa address \"%s\"; "
+ "sending error.",
+ escaped_safe_str(exitconn->_base.address));
+#else
+ if (1) {
+ log_info(LD_PROTOCOL, "Dnsworker code does not support in-addr.arpa "
+ "domain, but received a request for \"%s\"; sending error.",
+ escaped_safe_str(exitconn->_base.address));
+#endif
+
+ if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE)
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
+ circ = circuit_get_by_edge_conn(exitconn);
+ if (circ)
+ circuit_detach_stream(circ, exitconn);
+ if (!exitconn->_base.marked_for_close)
+ connection_free(TO_CONN(exitconn));
+ return -1;
+ }
+ //log_notice(LD_EXIT, "Looks like an address %s",
+ // exitconn->_base.address);
+ }
/* now check the hash table to see if 'address' is already there. */
- strlcpy(search.address, exitconn->address, sizeof(search.address));
+ strlcpy(search.address, exitconn->_base.address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
- if (resolve) { /* already there */
+ if (resolve && resolve->expire > now) { /* already there */
switch (resolve->state) {
case CACHE_STATE_PENDING:
/* add us to the pending list */
@@ -301,121 +591,124 @@ dns_resolve(connection_t *exitconn)
pending_connection->next = resolve->pending_connections;
resolve->pending_connections = pending_connection;
log_debug(LD_EXIT,"Connection (fd %d) waiting for pending DNS "
- "resolve of %s",
- exitconn->s, escaped_safe_str(exitconn->address));
- exitconn->state = EXIT_CONN_STATE_RESOLVING;
+ "resolve of %s", exitconn->_base.s,
+ escaped_safe_str(exitconn->_base.address));
+ exitconn->_base.state = EXIT_CONN_STATE_RESOLVING;
return 0;
- case CACHE_STATE_VALID:
- exitconn->addr = resolve->addr;
+ case CACHE_STATE_CACHED_VALID:
log_debug(LD_EXIT,"Connection (fd %d) found cached answer for %s",
- exitconn->s, escaped_safe_str(exitconn->address));
- if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
- send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
+ exitconn->_base.s,
+ escaped_safe_str(resolve->address));
+ exitconn->address_ttl = resolve->ttl;
+ if (resolve->is_reverse) {
+ tor_assert(is_resolve);
+ send_resolved_hostname_cell(exitconn, resolve->result.hostname);
+ } else {
+ exitconn->_base.addr = resolve->result.addr;
+ if (is_resolve)
+ send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
+ }
return 1;
- case CACHE_STATE_FAILED:
+ case CACHE_STATE_CACHED_FAILED:
log_debug(LD_EXIT,"Connection (fd %d) found cached error for %s",
- exitconn->s, escaped_safe_str(exitconn->address));
- if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
+ exitconn->_base.s,
+ escaped_safe_str(exitconn->_base.address));
+ if (is_resolve)
send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
circ = circuit_get_by_edge_conn(exitconn);
if (circ)
circuit_detach_stream(circ, exitconn);
- if (!exitconn->marked_for_close)
- connection_free(exitconn);
+ if (!exitconn->_base.marked_for_close)
+ connection_free(TO_CONN(exitconn));
return -1;
+ case CACHE_STATE_DONE:
+ log_err(LD_BUG, "Found a 'DONE' dns resolve still in the cache.");
+ tor_fragile_assert();
}
tor_assert(0);
}
/* not there, need to add it */
resolve = tor_malloc_zero(sizeof(cached_resolve_t));
+ resolve->magic = CACHED_RESOLVE_MAGIC;
resolve->state = CACHE_STATE_PENDING;
- resolve->expire = now + MAX_DNS_ENTRY_AGE;
- strlcpy(resolve->address, exitconn->address, sizeof(resolve->address));
+ resolve->is_reverse = is_reverse;
+ strlcpy(resolve->address, exitconn->_base.address, sizeof(resolve->address));
- /* add us to the pending list */
+ /* add this connection to the pending list */
pending_connection = tor_malloc_zero(sizeof(pending_connection_t));
pending_connection->conn = exitconn;
resolve->pending_connections = pending_connection;
- exitconn->state = EXIT_CONN_STATE_RESOLVING;
+ exitconn->_base.state = EXIT_CONN_STATE_RESOLVING;
- insert_resolve(resolve);
- return assign_to_dnsworker(exitconn);
+ /* Add this resolve to the cache and priority queue. */
+ HT_INSERT(cache_map, &cache_root, resolve);
+ set_expiry(resolve, now + RESOLVE_MAX_TIMEOUT);
+
+ log_debug(LD_EXIT,"Launching %s.",
+ escaped_safe_str(exitconn->_base.address));
+ assert_cache_ok();
+ return launch_resolve(exitconn);
}
-/** Find or spawn a dns worker process to handle resolving
- * <b>exitconn</b>-\>address; tell that dns worker to begin resolving.
+/** Log an error and abort if conn is waiting for a DNS resolve.
*/
-static int
-assign_to_dnsworker(connection_t *exitconn)
+void
+assert_connection_edge_not_dns_pending(edge_connection_t *conn)
{
- connection_t *dnsconn;
- unsigned char len;
-
- tor_assert(exitconn->state == EXIT_CONN_STATE_RESOLVING);
- tor_assert(exitconn->s == -1);
+ pending_connection_t *pend;
+ cached_resolve_t **resolve;
- /* respawn here, to be sure there are enough */
- if (spawn_enough_dnsworkers() < 0) {
- goto err;
+ HT_FOREACH(resolve, cache_map, &cache_root) {
+ for (pend = (*resolve)->pending_connections;
+ pend;
+ pend = pend->next) {
+ tor_assert(pend->conn != conn);
+ }
}
+}
- dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER,
- DNSWORKER_STATE_IDLE);
+/** Log an error and abort if any connection waiting for a DNS resolve is
+ * corrupted. */
+void
+assert_all_pending_dns_resolves_ok(void)
+{
+ pending_connection_t *pend;
+ cached_resolve_t **resolve;
- if (!dnsconn) {
- log_warn(LD_EXIT,"no idle dns workers. Failing.");
- if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
- send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
- goto err;
+ HT_FOREACH(resolve, cache_map, &cache_root) {
+ for (pend = (*resolve)->pending_connections;
+ pend;
+ pend = pend->next) {
+ assert_connection_ok(TO_CONN(pend->conn), 0);
+ tor_assert(pend->conn->_base.s == -1);
+ tor_assert(!connection_in_array(TO_CONN(pend->conn)));
+ }
}
-
- log_debug(LD_EXIT,
- "Connection (fd %d) needs to resolve %s; assigning "
- "to DNSWorker (fd %d)", exitconn->s,
- escaped_safe_str(exitconn->address), dnsconn->s);
-
- tor_free(dnsconn->address);
- dnsconn->address = tor_strdup(exitconn->address);
- dnsconn->state = DNSWORKER_STATE_BUSY;
- /* touch the lastwritten timestamp, since that's how we check to
- * see how long it's been since we asked the question, and sometimes
- * we check before the first call to connection_handle_write(). */
- dnsconn->timestamp_lastwritten = time(NULL);
- num_dnsworkers_busy++;
-
- len = strlen(dnsconn->address);
- connection_write_to_buf((char*)&len, 1, dnsconn);
- connection_write_to_buf(dnsconn->address, len, dnsconn);
-
- return 0;
-err:
- dns_cancel_pending_resolve(exitconn->address); /* also sends end and frees */
- return -1;
}
/** Remove <b>conn</b> from the list of connections waiting for conn-\>address.
*/
void
-connection_dns_remove(connection_t *conn)
+connection_dns_remove(edge_connection_t *conn)
{
pending_connection_t *pend, *victim;
cached_resolve_t search;
cached_resolve_t *resolve;
- tor_assert(conn->type == CONN_TYPE_EXIT);
- tor_assert(conn->state == EXIT_CONN_STATE_RESOLVING);
+ tor_assert(conn->_base.type == CONN_TYPE_EXIT);
+ tor_assert(conn->_base.state == EXIT_CONN_STATE_RESOLVING);
- strlcpy(search.address, conn->address, sizeof(search.address));
+ strlcpy(search.address, conn->_base.address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
if (!resolve) {
log_notice(LD_BUG, "Address %s is not pending. Dropping.",
- escaped_safe_str(conn->address));
+ escaped_safe_str(conn->_base.address));
return;
}
tor_assert(resolve->pending_connections);
- assert_connection_ok(conn,0);
+ assert_connection_ok(TO_CONN(conn),0);
pend = resolve->pending_connections;
@@ -424,7 +717,7 @@ connection_dns_remove(connection_t *conn)
tor_free(pend);
log_debug(LD_EXIT, "First connection (fd %d) no longer waiting "
"for resolve of %s",
- conn->s, escaped_safe_str(conn->address));
+ conn->_base.s, escaped_safe_str(conn->_base.address));
return;
} else {
for ( ; pend->next; pend = pend->next) {
@@ -434,7 +727,7 @@ connection_dns_remove(connection_t *conn)
tor_free(victim);
log_debug(LD_EXIT,
"Connection (fd %d) no longer waiting for resolve of %s",
- conn->s, escaped_safe_str(conn->address));
+ conn->_base.s, escaped_safe_str(conn->_base.address));
return; /* more are pending */
}
}
@@ -442,59 +735,23 @@ connection_dns_remove(connection_t *conn)
}
}
-/** Log an error and abort if conn is waiting for a DNS resolve.
- */
-void
-assert_connection_edge_not_dns_pending(connection_t *conn)
-{
- pending_connection_t *pend;
- cached_resolve_t **resolve;
-
- HT_FOREACH(resolve, cache_map, &cache_root) {
- for (pend = (*resolve)->pending_connections;
- pend;
- pend = pend->next) {
- tor_assert(pend->conn != conn);
- }
- }
-}
-
-/** Log an error and abort if any connection waiting for a DNS resolve is
- * corrupted. */
-void
-assert_all_pending_dns_resolves_ok(void)
-{
- pending_connection_t *pend;
- cached_resolve_t **resolve;
-
- HT_FOREACH(resolve, cache_map, &cache_root) {
- for (pend = (*resolve)->pending_connections;
- pend;
- pend = pend->next) {
- assert_connection_ok(pend->conn, 0);
- tor_assert(pend->conn->s == -1);
- tor_assert(!connection_in_array(pend->conn));
- }
- }
-}
-
/** Mark all connections waiting for <b>address</b> for close. Then cancel
* the resolve for <b>address</b> itself, and remove any cached results for
* <b>address</b> from the cache.
*/
void
-dns_cancel_pending_resolve(char *address)
+dns_cancel_pending_resolve(const char *address)
{
pending_connection_t *pend;
cached_resolve_t search;
- cached_resolve_t *resolve;
- connection_t *pendconn;
+ cached_resolve_t *resolve, *tmp;
+ edge_connection_t *pendconn;
circuit_t *circ;
strlcpy(search.address, address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
- if (!resolve) {
+ if (!resolve || resolve->state != CACHE_STATE_PENDING) {
log_notice(LD_BUG,"Address %s is not pending. Dropping.",
escaped_safe_str(address));
return;
@@ -516,84 +773,104 @@ dns_cancel_pending_resolve(char *address)
escaped_safe_str(address));
while (resolve->pending_connections) {
pend = resolve->pending_connections;
- pend->conn->state = EXIT_CONN_STATE_RESOLVEFAILED;
+ pend->conn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
pendconn = pend->conn;
- tor_assert(pendconn->s == -1);
- if (!pendconn->marked_for_close) {
+ assert_connection_ok(TO_CONN(pendconn), 0);
+ tor_assert(pendconn->_base.s == -1);
+ if (!pendconn->_base.marked_for_close) {
connection_edge_end(pendconn, END_STREAM_REASON_RESOURCELIMIT,
pendconn->cpath_layer);
}
circ = circuit_get_by_edge_conn(pendconn);
if (circ)
circuit_detach_stream(circ, pendconn);
- connection_free(pendconn);
+ connection_free(TO_CONN(pendconn));
resolve->pending_connections = pend->next;
tor_free(pend);
}
- dns_purge_resolve(resolve);
+ tmp = HT_REMOVE(cache_map, &cache_root, resolve);
+ if (tmp != resolve) {
+ log_err(LD_BUG, "The cancelled resolve we purged didn't match any in"
+ " the cache. Tried to purge %s (%p); instead got %s (%p).",
+ resolve->address, (void*)resolve,
+ tmp ? tmp->address : "NULL", (void*)tmp);
+ }
+ tor_assert(tmp == resolve);
+
+ resolve->state = CACHE_STATE_DONE;
}
-/** Remove <b>resolve</b> from the cache.
- */
+/** Helper: adds an entry to the DNS cache mapping <b>address</b> to the ipv4
+ * address <b>addr</b> (if is_reverse is 0) or the hostname <b>hostname</b> if
+ * (is_reverse is 1). <b>ttl</b> is a cache ttl; <b>outcome</b> is one of
+ * DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
+ **/
static void
-dns_purge_resolve(cached_resolve_t *resolve)
+add_answer_to_cache(const char *address, int is_reverse, uint32_t addr,
+ const char *hostname, char outcome, uint32_t ttl)
{
- cached_resolve_t *tmp;
-
- /* remove resolve from the linked list */
- if (resolve == oldest_cached_resolve) {
- oldest_cached_resolve = resolve->next;
- if (oldest_cached_resolve == NULL)
- newest_cached_resolve = NULL;
- } else {
- /* FFFF make it a doubly linked list if this becomes too slow */
- for (tmp=oldest_cached_resolve; tmp && tmp->next != resolve; tmp=tmp->next)
- ;
- tor_assert(tmp); /* it's got to be in the list, or we screwed up somewhere
- * else */
- tmp->next = resolve->next; /* unlink it */
+ cached_resolve_t *resolve;
+ if (outcome == DNS_RESOLVE_FAILED_TRANSIENT)
+ return;
- if (newest_cached_resolve == resolve)
- newest_cached_resolve = tmp;
- }
+ /* XXX This is dumb, but it seems to workaround a bug I can't find. We
+ * should nail this so we can cache reverse DNS answers. -NM */
+ if (is_reverse)
+ return;
- /* remove resolve from the map */
- HT_REMOVE(cache_map, &cache_root, resolve);
+ //log_notice(LD_EXIT, "Adding to cache: %s -> %s (%lx, %s), %d",
+ // address, is_reverse?"(reverse)":"", (unsigned long)addr,
+ // hostname?hostname:"NULL",(int)outcome);
- tor_free(resolve);
+ resolve = tor_malloc_zero(sizeof(cached_resolve_t));
+ resolve->magic = CACHED_RESOLVE_MAGIC;
+ resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
+ CACHE_STATE_CACHED_VALID : CACHE_STATE_CACHED_FAILED;
+ strlcpy(resolve->address, address, sizeof(resolve->address));
+ resolve->is_reverse = is_reverse;
+ if (is_reverse) {
+ tor_assert(hostname);
+ resolve->result.hostname = tor_strdup(hostname);
+ } else {
+ tor_assert(!hostname);
+ resolve->result.addr = addr;
+ }
+ resolve->ttl = ttl;
+ assert_resolve_ok(resolve);
+ HT_INSERT(cache_map, &cache_root, resolve);
+ set_expiry(resolve, time(NULL) + dns_get_expiry_ttl(ttl));
}
-/** Called on the OR side when a DNS worker tells us the outcome of a DNS
- * resolve: tell all pending connections about the result of the lookup, and
- * cache the value. (<b>address</b> is a NUL-terminated string containing the
- * address to look up; <b>addr</b> is an IPv4 address in host order;
- * <b>outcome</b> is one of
+/** Called on the OR side when a DNS worker or the eventdns library tells us
+ * the outcome of a DNS resolve: tell all pending connections about the result
+ * of the lookup, and cache the value. (<b>address</b> is a NUL-terminated
+ * string containing the address to look up; <b>addr</b> is an IPv4 address in
+ * host order; <b>outcome</b> is one of
* DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
*/
static void
-dns_found_answer(char *address, uint32_t addr, char outcome)
+dns_found_answer(const char *address, int is_reverse, uint32_t addr,
+ const char *hostname, char outcome, uint32_t ttl)
{
pending_connection_t *pend;
cached_resolve_t search;
- cached_resolve_t *resolve;
- connection_t *pendconn;
+ cached_resolve_t *resolve, *removed;
+ edge_connection_t *pendconn;
circuit_t *circ;
+ assert_cache_ok();
+
strlcpy(search.address, address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
if (!resolve) {
log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
escaped_safe_str(address));
- resolve = tor_malloc_zero(sizeof(cached_resolve_t));
- resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
- CACHE_STATE_VALID : CACHE_STATE_FAILED;
- resolve->addr = addr;
- resolve->expire = time(NULL) + MAX_DNS_ENTRY_AGE;
- insert_resolve(resolve);
+ add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
return;
}
+ assert_resolve_ok(resolve);
if (resolve->state != CACHE_STATE_PENDING) {
/* XXXX Maybe update addr? or check addr for consistency? Or let
@@ -609,23 +886,18 @@ dns_found_answer(char *address, uint32_t addr, char outcome)
* resolve X.Y.Z. */
/* tor_assert(resolve->state == CACHE_STATE_PENDING); */
- resolve->addr = addr;
- if (outcome == DNS_RESOLVE_SUCCEEDED)
- resolve->state = CACHE_STATE_VALID;
- else
- resolve->state = CACHE_STATE_FAILED;
-
while (resolve->pending_connections) {
pend = resolve->pending_connections;
- assert_connection_ok(pend->conn,time(NULL));
- pend->conn->addr = resolve->addr;
pendconn = pend->conn; /* don't pass complex things to the
connection_mark_for_close macro */
+ assert_connection_ok(TO_CONN(pendconn),time(NULL));
+ pendconn->_base.addr = addr;
+ pendconn->address_ttl = ttl;
- if (resolve->state == CACHE_STATE_FAILED) {
+ if (outcome != DNS_RESOLVE_SUCCEEDED) {
/* prevent double-remove. */
- pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
- if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
+ pendconn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
+ if (pendconn->_base.purpose == EXIT_PURPOSE_CONNECT) {
connection_edge_end(pendconn, END_STREAM_REASON_RESOLVEFAILED,
pendconn->cpath_layer);
/* This detach must happen after we send the end cell. */
@@ -635,40 +907,109 @@ dns_found_answer(char *address, uint32_t addr, char outcome)
/* This detach must happen after we send the resolved cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
}
- connection_free(pendconn);
+ connection_free(TO_CONN(pendconn));
} else {
- if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
+ if (pendconn->_base.purpose == EXIT_PURPOSE_CONNECT) {
+ tor_assert(!is_reverse);
/* prevent double-remove. */
- pend->conn->state = EXIT_CONN_STATE_CONNECTING;
+ pend->conn->_base.state = EXIT_CONN_STATE_CONNECTING;
circ = circuit_get_by_edge_conn(pend->conn);
tor_assert(circ);
+ tor_assert(!CIRCUIT_IS_ORIGIN(circ));
/* unlink pend->conn from resolving_streams, */
circuit_detach_stream(circ, pend->conn);
/* and link it to n_streams */
- pend->conn->next_stream = circ->n_streams;
+ pend->conn->next_stream = TO_OR_CIRCUIT(circ)->n_streams;
pend->conn->on_circuit = circ;
- circ->n_streams = pend->conn;
+ TO_OR_CIRCUIT(circ)->n_streams = pend->conn;
connection_exit_connect(pend->conn);
} else {
/* prevent double-remove. This isn't really an accurate state,
* but it does the right thing. */
- pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
- send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
+ pendconn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
+ if (is_reverse)
+ send_resolved_hostname_cell(pendconn, hostname);
+ else
+ send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ);
circuit_detach_stream(circ, pendconn);
- connection_free(pendconn);
+ connection_free(TO_CONN(pendconn));
}
}
resolve->pending_connections = pend->next;
tor_free(pend);
}
- if (outcome == DNS_RESOLVE_FAILED_TRANSIENT) { /* remove from cache */
- dns_purge_resolve(resolve);
+ resolve->state = CACHE_STATE_DONE;
+ removed = HT_REMOVE(cache_map, &cache_root, &search);
+ if (removed != resolve) {
+ log_err(LD_BUG, "The pending resolve we found wasn't removable from"
+ " the cache. Tried to purge %s (%p); instead got %s (%p).",
+ resolve->address, (void*)resolve,
+ removed ? removed->address : "NULL", (void*)removed);
}
+ assert_resolve_ok(resolve);
+ assert_cache_ok();
+
+ add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
+ assert_cache_ok();
+}
+
+#ifndef USE_EVENTDNS
+/** Find or spawn a dns worker process to handle resolving
+ * <b>exitconn</b>-\>address; tell that dns worker to begin resolving.
+ */
+static int
+launch_resolve(edge_connection_t *exitconn)
+{
+ connection_t *dnsconn;
+ unsigned char len;
+
+ tor_assert(exitconn->_base.state == EXIT_CONN_STATE_RESOLVING);
+ assert_connection_ok(TO_CONN(exitconn), 0);
+ tor_assert(exitconn->_base.s == -1);
+
+ /* respawn here, to be sure there are enough */
+ if (spawn_enough_dnsworkers() < 0) {
+ goto err;
+ }
+
+ dnsconn = connection_get_by_type_state(CONN_TYPE_DNSWORKER,
+ DNSWORKER_STATE_IDLE);
+
+ if (!dnsconn) {
+ log_warn(LD_EXIT,"no idle dns workers. Failing.");
+ if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE)
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
+ goto err;
+ }
+
+ log_debug(LD_EXIT,
+ "Connection (fd %d) needs to resolve %s; assigning "
+ "to DNSWorker (fd %d)", exitconn->_base.s,
+ escaped_safe_str(exitconn->_base.address), dnsconn->s);
+
+ tor_free(dnsconn->address);
+ dnsconn->address = tor_strdup(exitconn->_base.address);
+ dnsconn->state = DNSWORKER_STATE_BUSY;
+ /* touch the lastwritten timestamp, since that's how we check to
+ * see how long it's been since we asked the question, and sometimes
+ * we check before the first call to connection_handle_write(). */
+ dnsconn->timestamp_lastwritten = time(NULL);
+ num_dnsworkers_busy++;
+
+ len = strlen(dnsconn->address);
+ connection_write_to_buf((char*)&len, 1, dnsconn);
+ connection_write_to_buf(dnsconn->address, len, dnsconn);
+
+ return 0;
+err:
+ /* also sends end and frees */
+ dns_cancel_pending_resolve(exitconn->_base.address);
+ return -1;
}
/******************************************************************/
@@ -687,6 +1028,8 @@ connection_dns_finished_flushing(connection_t *conn)
return 0;
}
+/** Called when a connection to a dnsworker hits an EOF; this only happens
+ * when a dnsworker dies unexpectedly. */
int
connection_dns_reached_eof(connection_t *conn)
{
@@ -711,6 +1054,7 @@ connection_dns_process_inbuf(connection_t *conn)
{
char success;
uint32_t addr;
+ int ttl;
tor_assert(conn);
tor_assert(conn->type == CONN_TYPE_DNSWORKER);
@@ -746,7 +1090,9 @@ connection_dns_process_inbuf(connection_t *conn)
tor_assert(success >= DNS_RESOLVE_FAILED_TRANSIENT);
tor_assert(success <= DNS_RESOLVE_SUCCEEDED);
- dns_found_answer(conn->address, ntohl(addr), success);
+
+ ttl = (success == DNS_RESOLVE_FAILED_TRANSIENT) ? 0 : MAX_DNS_ENTRY_AGE;
+ dns_found_answer(conn->address, 0, ntohl(addr), NULL, success, ttl);
tor_free(conn->address);
conn->address = tor_strdup("<idle>");
@@ -763,7 +1109,7 @@ connection_dns_process_inbuf(connection_t *conn)
/** Close and re-open all idle dnsworkers; schedule busy ones to be closed
* and re-opened once they're no longer busy.
**/
-void
+static void
dnsworkers_rotate(void)
{
connection_t *dnsconn;
@@ -794,10 +1140,10 @@ dnsworkers_rotate(void)
* The dnsworker runs indefinitely, until its connection is closed or an error
* occurs.
*/
-static int
+static void
dnsworker_main(void *data)
{
- char address[MAX_ADDRESSLEN];
+ char address[MAX_ADDRESSLEN+1]; /* Plus a byte for a final '.' */
unsigned char address_len;
char *log_address;
char answer[5];
@@ -805,6 +1151,7 @@ dnsworker_main(void *data)
int *fdarray = data;
int fd;
int result;
+ int search = get_options()->ServerDNSSearchDomains;
/* log_fn(LOG_NOTICE,"After spawn: fdarray @%d has %d:%d", (int)fdarray,
* fdarray[0],fdarray[1]); */
@@ -842,7 +1189,13 @@ dnsworker_main(void *data)
crypto_thread_cleanup();
spawn_exit();
}
- address[address_len] = 0; /* null terminate it */
+ /* Add a period to prevent local domain search, and NUL-terminate. */
+ if (address[address_len-1] != '.' && !search) {
+ address[address_len] = '.';
+ address[address_len+1] = '\0';
+ } else {
+ address[address_len] = '\0';
+ }
log_address = esc_for_log(safe_str(address));
result = tor_lookup_hostname(address, &ip);
@@ -852,17 +1205,17 @@ dnsworker_main(void *data)
switch (result) {
case 1:
/* XXX result can never be 1, because we set it to -1 above on error */
- log_info(LD_NET,"Could not resolve dest addr %s (transient).",
+ log_info(LD_NET,"Could not resolve dest addr %s (transient)",
log_address);
answer[0] = DNS_RESOLVE_FAILED_TRANSIENT;
break;
case -1:
- log_info(LD_NET,"Could not resolve dest addr %s (permanent).",
+ log_info(LD_NET,"Could not resolve dest addr %s (permanent)",
log_address);
answer[0] = DNS_RESOLVE_FAILED_PERMANENT;
break;
case 0:
- log_info(LD_NET,"Resolved address %s.", log_address);
+ log_info(LD_NET,"Resolved address %s", log_address);
answer[0] = DNS_RESOLVE_SUCCEEDED;
break;
}
@@ -875,7 +1228,6 @@ dnsworker_main(void *data)
spawn_exit();
}
}
- return 0; /* windows wants this function to return an int */
}
/** Launch a new DNS worker; return 0 on success, -1 on failure.
@@ -896,12 +1248,15 @@ spawn_dnsworker(void)
return -1;
}
+ tor_assert(fdarray[0] >= 0);
+ tor_assert(fdarray[1] >= 0);
+
/* log_fn(LOG_NOTICE,"Before spawn: fdarray @%d has %d:%d",
(int)fdarray, fdarray[0],fdarray[1]); */
fd = fdarray[0]; /* We copy this out here, since dnsworker_main may free
* fdarray */
- spawn_func(dnsworker_main, (void*)fdarray);
+ spawn_func((void*) dnsworker_main, (void*)fdarray);
log_debug(LD_EXIT,"just spawned a dns worker.");
#ifndef TOR_IS_MULTITHREADED
tor_close_socket(fdarray[1]); /* don't need the worker's side of the pipe */
@@ -992,3 +1347,424 @@ spawn_enough_dnsworkers(void)
return 0;
}
+void
+dns_launch_wildcard_checks(void)
+{
+}
+#else /* !USE_EVENTDNS */
+
+/** Eventdns helper: return true iff the eventdns result <b>err</b> is
+ * a transient failure. */
+static int
+eventdns_err_is_transient(int err)
+{
+ switch (err)
+ {
+ case DNS_ERR_SERVERFAILED:
+ case DNS_ERR_TRUNCATED:
+ case DNS_ERR_TIMEOUT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+/* Dummy function; never called with eventdns enabled. */
+int
+connection_dns_finished_flushing(connection_t *conn)
+{
+ (void)conn;
+ tor_assert(0);
+ return 0;
+}
+/* Dummy function; never called with eventdns enabled. */
+int
+connection_dns_process_inbuf(connection_t *conn)
+{
+ (void)conn;
+ tor_assert(0);
+ return 0;
+}
+/* Dummy function; never called with eventdns enabled. */
+int
+connection_dns_reached_eof(connection_t *conn)
+{
+ (void)conn;
+ tor_assert(0);
+ return 0;
+}
+
+/** Configure eventdns nameservers if force is true, or if the configuration
+ * has changed since the last time we called this function. On Unix, this
+ * reads from options->ServerDNSResolvConfFile or /etc/resolv.conf; on
+ * Windows, this reads from options->ServerDNSResolvConfFile or the registry.
+ * Return 0 on success or -1 on failure. */
+static int
+configure_nameservers(int force)
+{
+ or_options_t *options;
+ const char *conf_fname;
+ struct stat st;
+ options = get_options();
+ conf_fname = options->ServerDNSResolvConfFile;
+#ifndef MS_WINDOWS
+ if (!conf_fname)
+ conf_fname = "/etc/resolv.conf";
+#endif
+
+ eventdns_set_log_fn(eventdns_log_cb);
+ if (conf_fname) {
+ if (stat(conf_fname, &st)) {
+ log_warn(LD_EXIT, "Unable to stat resolver configuration in '%s'",
+ conf_fname);
+ return -1;
+ }
+ if (!force && resolv_conf_fname && !strcmp(conf_fname,resolv_conf_fname)
+ && st.st_mtime == resolv_conf_mtime) {
+ log_info(LD_EXIT, "No change to '%s'", conf_fname);
+ return 0;
+ }
+ if (nameservers_configured) {
+ eventdns_search_clear();
+ eventdns_clear_nameservers_and_suspend();
+ }
+ log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname);
+ if (eventdns_resolv_conf_parse(DNS_OPTIONS_ALL, conf_fname))
+ return -1;
+ if (eventdns_count_nameservers() == 0) {
+ log_warn(LD_EXIT, "Unable to find any nameservers in '%s'.", conf_fname);
+ return -1;
+ }
+ tor_free(resolv_conf_fname);
+ resolv_conf_fname = tor_strdup(conf_fname);
+ resolv_conf_mtime = st.st_mtime;
+ if (nameservers_configured)
+ eventdns_resume();
+ }
+#ifdef MS_WINDOWS
+ else {
+ if (nameservers_configured) {
+ eventdns_search_clear();
+ eventdns_clear_nameservers_and_suspend();
+ }
+ if (eventdns_config_windows_nameservers()) {
+ log_warn(LD_EXIT,"Could not config nameservers.");
+ return -1;
+ }
+ if (eventdns_count_nameservers() == 0) {
+ log_warn(LD_EXIT, "Unable to find any platform nameservers in "
+ "your Windows configuration. Perhaps you should list a "
+ "ServerDNSResolvConfFile file in your torrc?");
+ return -1;
+ }
+ if (nameservers_configured)
+ eventdns_resume();
+ tor_free(resolv_conf_fname);
+ resolv_conf_mtime = 0;
+ }
+#endif
+
+ nameservers_configured = 1;
+ return 0;
+}
+
+/** For eventdns: Called when we get an answer for a request we launched.
+ * See eventdns.h for arguments; 'arg' holds the address we tried to resolve.
+ */
+static void
+eventdns_callback(int result, char type, int count, int ttl, void *addresses,
+ void *arg)
+{
+ char *string_address = arg;
+ int is_reverse = 0;
+ int status = DNS_RESOLVE_FAILED_PERMANENT;
+ uint32_t addr = 0;
+ const char *hostname = NULL;
+
+ if (result == DNS_ERR_NONE) {
+ if (type == DNS_IPv4_A && count) {
+ char answer_buf[INET_NTOA_BUF_LEN+1];
+ struct in_addr in;
+ char *escaped_address;
+ uint32_t *addrs = addresses;
+ in.s_addr = addrs[0];
+ addr = ntohl(addrs[0]);
+ status = DNS_RESOLVE_SUCCEEDED;
+ tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
+ escaped_address = esc_for_log(string_address);
+
+ if (answer_is_wildcarded(answer_buf)) {
+ log_debug(LD_EXIT, "eventdns said that %s resolves to ISP-hijacked "
+ "address %s; treating as a failure.",
+ safe_str(escaped_address),
+ escaped_safe_str(answer_buf));
+ addr = 0;
+ status = DNS_RESOLVE_FAILED_PERMANENT;
+ } else {
+ log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
+ safe_str(escaped_address),
+ escaped_safe_str(answer_buf));
+ }
+ tor_free(escaped_address);
+ } else if (type == DNS_PTR && count) {
+ char *escaped_address;
+ is_reverse = 1;
+ hostname = ((char**)addresses)[0];
+ status = DNS_RESOLVE_SUCCEEDED;
+ escaped_address = esc_for_log(string_address);
+ log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
+ safe_str(escaped_address),
+ escaped_safe_str(hostname));
+ tor_free(escaped_address);
+ } else if (count) {
+ log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.",
+ escaped_safe_str(string_address));
+ } else {
+ log_warn(LD_BUG, "eventdns returned no addresses or error for %s!",
+ escaped_safe_str(string_address));
+ }
+ } else {
+ if (eventdns_err_is_transient(result))
+ status = DNS_RESOLVE_FAILED_TRANSIENT;
+ }
+ dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
+ tor_free(string_address);
+}
+
+/** For eventdns: start resolving as necessary to find the target for
+ * <b>exitconn</b> */
+static int
+launch_resolve(edge_connection_t *exitconn)
+{
+ char *addr = tor_strdup(exitconn->_base.address);
+ struct in_addr in;
+ int r;
+ int options = get_options()->ServerDNSSearchDomains ? 0
+ : DNS_QUERY_NO_SEARCH;
+ /* What? Nameservers not configured? Sounds like a bug. */
+ if (!nameservers_configured) {
+ log_warn(LD_EXIT, "Harmless bug: nameservers not configured, but resolve "
+ "launched. Configuring.");
+ if (configure_nameservers(1) < 0)
+ return -1;
+ }
+
+ r = parse_inaddr_arpa_address(exitconn->_base.address, &in);
+ if (r == 0) {
+ log_info(LD_EXIT, "Launching eventdns request for %s",
+ escaped_safe_str(exitconn->_base.address));
+ r = eventdns_resolve_ipv4(exitconn->_base.address, options,
+ eventdns_callback, addr);
+ } else if (r == 1) {
+ log_info(LD_EXIT, "Launching eventdns reverse request for %s",
+ escaped_safe_str(exitconn->_base.address));
+ r = eventdns_resolve_reverse(&in, DNS_QUERY_NO_SEARCH,
+ eventdns_callback, addr);
+ } else if (r == -1) {
+ log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here.");
+ }
+
+ if (r) {
+ log_warn(LD_EXIT, "eventdns rejected address %s: error %d.",
+ escaped_safe_str(addr), r);
+ if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE) {
+ if (eventdns_err_is_transient(r))
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
+ else {
+ exitconn->address_ttl = DEFAULT_DNS_TTL;
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
+ }
+ }
+ dns_cancel_pending_resolve(addr); /* also sends end and frees */
+ tor_free(addr);
+ }
+ return r ? -1 : 0;
+}
+
+/** How many requests for bogus addresses have we launched so far? */
+static int n_wildcard_requests = 0;
+
+/** Map from dotted-quad IP address in response to an int holding how many
+ * times we've seen it for a randomly generated (hopefully bogus) address. It
+ * would be easier to use definitely-invalid addresses (as specified by
+ * RFC2606), but see comment in dns_launch_wildcard_checks(). */
+static strmap_t *dns_wildcard_response_count = NULL;
+
+/** If present, a list of dotted-quad IP addresses that we are pretty sure our
+ * nameserver wants to return in response to requests for nonexistent domains.
+ */
+static smartlist_t *dns_wildcard_list = NULL;
+
+/** Called when we see <b>id</b> (a dotted quad) in response to a request for
+ * a hopefully bogus address. */
+static void
+wildcard_increment_answer(const char *id)
+{
+ int *ip;
+ static int notice_given = 0;
+ if (!dns_wildcard_response_count)
+ dns_wildcard_response_count = strmap_new();
+
+ ip = strmap_get(dns_wildcard_response_count, id); // may be null (0)
+ if (!ip) {
+ ip = tor_malloc_zero(sizeof(int));
+ strmap_set(dns_wildcard_response_count, id, ip);
+ }
+ ++*ip;
+
+ if (*ip > 5 && n_wildcard_requests > 10) {
+ if (!dns_wildcard_list) dns_wildcard_list = smartlist_create();
+ if (!smartlist_string_isin(dns_wildcard_list, id)) {
+ log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+ "Your DNS provider has given \"%s\" as an answer for %d different "
+ "invalid addresses. Apparently they are hijacking DNS failures. "
+ "I'll trying to correct for this by treating future occurrences of "
+ "\"%s\" as 'not found'.", id, *ip, id);
+ smartlist_add(dns_wildcard_list, tor_strdup(id));
+ }
+ }
+}
+
+/** Callback function when we get an answer (possibly failing) for a request
+ * for a (hopefully) nonexistent domain. */
+static void
+eventdns_wildcard_check_callback(int result, char type, int count, int ttl,
+ void *addresses, void *arg)
+{
+ static int notice_given = 0;
+ (void)ttl;
+ ++n_wildcard_requests;
+ if (result == DNS_ERR_NONE && type == DNS_IPv4_A && count) {
+ uint32_t *addrs = addresses;
+ int i;
+ char *string_address = arg;
+ for (i = 0; i < count; ++i) {
+ char answer_buf[INET_NTOA_BUF_LEN+1];
+ struct in_addr in;
+ in.s_addr = addrs[i];
+ tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
+ wildcard_increment_answer(answer_buf);
+ }
+ log(notice_given ? LOG_INFO : LOG_NOTICE, LD_EXIT,
+ "Your DNS provider gave an answer for \"%s\", which "
+ "is not supposed to exist. Apparently they are hijacking "
+ "DNS failures. Trying to correct for this. We've noticed %d possibly "
+ "bad addresses so far.",
+ string_address, strmap_size(dns_wildcard_response_count));
+ notice_given = 1;
+ }
+ tor_free(arg);
+}
+
+/** Launch a single request for a nonexistent hostname consisting of between
+ * <b>min_len</b> and <b>max_len</b> random (plausible) characters followed by
+ * <b>suffix</b> */
+static void
+launch_wildcard_check(int min_len, int max_len, const char *suffix)
+{
+ char random_bytes[20], name[64], *addr;
+ size_t len;
+ int r;
+
+ len = min_len + crypto_rand_int(max_len-min_len+1);
+ if (crypto_rand(random_bytes, sizeof(random_bytes)) < 0)
+ return;
+ base32_encode(name, sizeof(name), random_bytes, sizeof(random_bytes));
+ name[len] = '\0';
+ strlcat(name, suffix, sizeof(name));
+
+ addr = tor_strdup(name);
+ r = eventdns_resolve_ipv4(name, DNS_QUERY_NO_SEARCH,
+ eventdns_wildcard_check_callback, addr);
+ if (r)
+ tor_free(addr);
+}
+
+#define N_WILDCARD_CHECKS 2
+
+/** Launch DNS requests for a few nonexistent hostnames, and see if we can
+ * catch our nameserver trying to hijack them and map them to a stupid "I
+ * couldn't find ggoogle.com but maybe you'd like to buy these lovely
+ * encyclopedias" page. */
+void
+dns_launch_wildcard_checks(void)
+{
+ int i;
+ if (!get_options()->ServerDNSDetectHijacking)
+ return;
+ log_info(LD_EXIT, "Launching checks to see whether our nameservers like "
+ "to hijack DNS failures.");
+ for (i = 0; i < N_WILDCARD_CHECKS; ++i) {
+ /* RFC2606 reserves these. Sadly, some DNS hijackers, in a silly attempt
+ * to 'comply' with rfc2606, refrain from giving A records for these.
+ * This is the standards-complaince equivalent of making sure that your
+ * crackhouse's elevator inspection certificate is up to date.
+ */
+ launch_wildcard_check(2, 16, "%s.invalid");
+ launch_wildcard_check(2, 16, "%s.test");
+
+ /* Thy somese will break specs if there are ever any number of
+ * 8+-character top-level domains. */
+ launch_wildcard_check(8, 16,"");
+
+ /* Try some random .com/org/net domains. This will work fine so long as
+ * not too many resolve to the same place. */
+ launch_wildcard_check(8, 16, "%s.com");
+ launch_wildcard_check(8, 16, "%s.org");
+ launch_wildcard_check(8, 16, "%s.net");
+ }
+}
+
+/** Return true iff we have noticed that the dotted-quad <b>ip</b> has been
+ * returned in response to requests for nonexistent hostnames. */
+static int
+answer_is_wildcarded(const char *ip)
+{
+ return dns_wildcard_list && smartlist_string_isin(dns_wildcard_list, ip);
+}
+#endif /* USE_EVENTDNS */
+
+/** Exit with an assertion if <b>resolve</b> is corrupt. */
+static void
+assert_resolve_ok(cached_resolve_t *resolve)
+{
+ tor_assert(resolve);
+ tor_assert(resolve->magic == CACHED_RESOLVE_MAGIC);
+ tor_assert(strlen(resolve->address) < MAX_ADDRESSLEN);
+ tor_assert(tor_strisnonupper(resolve->address));
+ if (resolve->state != CACHE_STATE_PENDING) {
+ tor_assert(!resolve->pending_connections);
+ }
+ if (resolve->state == CACHE_STATE_PENDING ||
+ resolve->state == CACHE_STATE_DONE) {
+ tor_assert(!resolve->ttl);
+ if (resolve->is_reverse)
+ tor_assert(!resolve->result.hostname);
+ else
+ tor_assert(!resolve->result.addr);
+ }
+}
+
+#ifdef DEBUG_DNS_CACHE
+/** Exit with an assertion if the DNS cache is corrupt. */
+static void
+_assert_cache_ok(void)
+{
+ cached_resolve_t **resolve;
+ int bad_rep = _cache_map_HT_REP_IS_BAD(&cache_root);
+ if (bad_rep) {
+ log_err(LD_BUG, "Bad rep type %d on dns cache hash table", bad_rep);
+ tor_assert(!bad_rep);
+ }
+
+ HT_FOREACH(resolve, cache_map, &cache_root) {
+ assert_resolve_ok(*resolve);
+ tor_assert((*resolve)->state != CACHE_STATE_DONE);
+ }
+ if (!cached_resolve_pqueue)
+ return;
+
+ smartlist_pqueue_assert_ok(cached_resolve_pqueue,
+ _compare_cached_resolves_by_expiry);
+}
+#endif
+
diff --git a/src/or/eventdns.c b/src/or/eventdns.c
new file mode 100644
index 0000000000..b9c60f0dbe
--- /dev/null
+++ b/src/or/eventdns.c
@@ -0,0 +1,2292 @@
+/* $Id$ */
+
+/* The original version of this module was written by Adam Langley; for
+ * a history of modifications, check out the subversion logs.
+ *
+ * When editing this module, try to keep it re-mergeable by Adam. Don't
+ * reformat the whitespace, add Tor dependencies, or so on.
+ *
+ * TODO:
+ * - Support AAAA records
+ * - Have a way to query for AAAA and A records simultaneously.
+ * - Improve request API.
+ * - (Can we suppress cnames? Should we?)
+ * - Replace all externally visible magic numbers with #defined constants.
+ * - Write documentation for APIs of all external functions.
+ */
+
+/* Async DNS Library
+ * Adam Langley <agl@imperialviolet.org>
+ * Public Domain code
+ *
+ * This software is Public Domain. To view a copy of the public domain dedication,
+ * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ *
+ * I ask and expect, but do not require, that all derivative works contain an
+ * attribution similar to:
+ * Parts developed by Adam Langley <agl@imperialviolet.org>
+ *
+ * You may wish to replace the word "Parts" with something else depending on
+ * the amount of original code.
+ *
+ * (Derivative works does not include programs which link against, run or include
+ * the source verbatim in their source distributions)
+ *
+ * Version: 0.1b
+ *
+ *
+ * Welcome, gentle reader
+ *
+ * Async DNS lookups are really a whole lot harder than they should be,
+ * mostly stemming from the fact that the libc resolver has never been
+ * very good at them. Before you use this library you should see if libc
+ * can do the job for you with the modern async call getaddrinfo_a
+ * (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
+ * please continue.
+ *
+ * This code is based on libevent and you must call event_init before
+ * any of the APIs in this file. You must also seed the OpenSSL random
+ * source if you are using OpenSSL for ids (see below).
+ *
+ * This library is designed to be included and shipped with your source
+ * code. You statically link with it. You should also test for the
+ * existence of strtok_r and define HAVE_STRTOK_R if you have it.
+ *
+ * The DNS protocol requires a good source of id numbers and these
+ * numbers should be unpredictable for spoofing reasons. There are
+ * three methods for generating them here and you must define exactly
+ * one of them. In increasing order of preference:
+ *
+ * DNS_USE_GETTIMEOFDAY_FOR_ID:
+ * Using the bottom 16 bits of the usec result from gettimeofday. This
+ * is a pretty poor solution but should work anywhere.
+ * DNS_USE_CPU_CLOCK_FOR_ID:
+ * Using the bottom 16 bits of the nsec result from the CPU's time
+ * counter. This is better, but may not work everywhere. Requires
+ * POSIX realtime support and you'll need to link against -lrt on
+ * glibc systems at least.
+ * DNS_USE_OPENSSL_FOR_ID:
+ * Uses the OpenSSL RAND_bytes call to generate the data. You must
+ * have seeded the pool before making any calls to this library.
+ *
+ * The library keeps track of the state of nameservers and will avoid
+ * them when they go down. Otherwise it will round robin between them.
+ *
+ * Quick start guide:
+ * #include "eventdns.h"
+ * void callback(int result, char type, int count, int ttl,
+ * void *addresses, void *arg);
+ * eventdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
+ * eventdns_resolve("www.hostname.com", 0, callback, NULL);
+ *
+ * When the lookup is complete the callback function is called. The
+ * first argument will be one of the DNS_ERR_* defines in eventdns.h.
+ * Hopefully it will be DNS_ERR_NONE, in which case type will be
+ * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
+ * which the data can be cached for (in seconds), addresses will point
+ * to an array of uint32_t's and arg will be whatever you passed to
+ * eventdns_resolve.
+ *
+ * Searching:
+ *
+ * In order for this library to be a good replacement for glibc's resolver it
+ * supports searching. This involves setting a list of default domains, in
+ * which names will be queried for. The number of dots in the query name
+ * determines the order in which this list is used.
+ *
+ * Searching appears to be a single lookup from the point of view of the API,
+ * although many DNS queries may be generated from a single call to
+ * eventdns_resolve. Searching can also drastically slow down the resolution
+ * of names.
+ *
+ * To disable searching:
+ * 1. Never set it up. If you never call eventdns_resolv_conf_parse or
+ * eventdns_search_add then no searching will occur.
+ *
+ * 2. If you do call eventdns_resolv_conf_parse then don't pass
+ * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it).
+ *
+ * 3. When calling eventdns_resolve, pass the DNS_QUERY_NO_SEARCH flag.
+ *
+ * The order of searches depends on the number of dots in the name. If the
+ * number is greater than the ndots setting then the names is first tried
+ * globally. Otherwise each search domain is appended in turn.
+ *
+ * The ndots setting can either be set from a resolv.conf, or by calling
+ * eventdns_search_ndots_set.
+ *
+ * For example, with ndots set to 1 (the default) and a search domain list of
+ * ["myhome.net"]:
+ * Query: www
+ * Order: www.myhome.net, www.
+ *
+ * Query: www.abc
+ * Order: www.abc., www.abc.myhome.net
+ *
+ * API reference:
+ *
+ * int eventdns_nameserver_add(unsigned long int address)
+ * Add a nameserver. The address should be an IP address in
+ * network byte order. The type of address is chosen so that
+ * it matches in_addr.s_addr.
+ * Returns non-zero on error.
+ *
+ * int eventdns_nameserver_ip_add(const char *ip_as_string)
+ * This wraps the above function by parsing a string as an IP
+ * address and adds it as a nameserver.
+ * Returns non-zero on error
+ *
+ * int eventdns_resolve(const char *name, int flags,
+ * eventdns_callback_type callback,
+ * void *ptr)
+ * Resolve a name. The name parameter should be a DNS name.
+ * The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
+ * which disables searching for this query. (see defn of
+ * searching above).
+ *
+ * The callback argument is a function which is called when
+ * this query completes and ptr is an argument which is passed
+ * to that callback function.
+ *
+ * Returns non-zero on error
+ *
+ * void eventdns_search_clear()
+ * Clears the list of search domains
+ *
+ * void eventdns_search_add(const char *domain)
+ * Add a domain to the list of search domains
+ *
+ * void eventdns_search_ndots_set(int ndots)
+ * Set the number of dots which, when found in a name, causes
+ * the first query to be without any search domain.
+ *
+ * int eventdns_count_nameservers(void)
+ * Return the number of configured nameservers (not necessarily the
+ * number of running nameservers). This is useful for double-checking
+ * whether our calls to the various nameserver configuration functions
+ * have been successful.
+ *
+ * int eventdns_clear_nameservers_and_suspend(void)
+ * Remove all currently configured nameservers, and suspend all pending
+ * resolves. Resolves will not necessarily be re-attempted until
+ * eventdns_resume() is called.
+ *
+ * int eventdns_resume(void)
+ * Re-attempt resolves left in limbo after an earlier call to
+ * eventdns_clear_nameservers_and_suspend().
+ *
+ * int eventdns_config_windows_nameservers(void)
+ * Attempt to configure a set of nameservers based on platform settings on
+ * a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
+ * looks in the registry. Returns 0 on success, nonzero on failure.
+ *
+ * int eventdns_resolv_conf_parse(int flags, const char *filename)
+ * Parse a resolv.conf like file from the given filename.
+ *
+ * See the man page for resolv.conf for the format of this file.
+ * The flags argument determines what information is parsed from
+ * this file:
+ * DNS_OPTION_SEARCH - domain, search and ndots options
+ * DNS_OPTION_NAMESERVERS - nameserver lines
+ * DNS_OPTION_MISC - timeout and attempts options
+ * DNS_OPTIONS_ALL - all of the above
+ * The following directives are not parsed from the file:
+ * sortlist, rotate, no-check-names, inet6, debug
+ *
+ * Returns non-zero on error:
+ * 0 no errors
+ * 1 failed to open file
+ * 2 failed to stat file
+ * 3 file too large
+ * 4 out of memory
+ * 5 short read from file
+ *
+ * Internals:
+ *
+ * Requests are kept in two queues. The first is the inflight queue. In
+ * this queue requests have an allocated transaction id and nameserver.
+ * They will soon be transmitted if they haven't already been.
+ *
+ * The second is the waiting queue. The size of the inflight ring is
+ * limited and all other requests wait in waiting queue for space. This
+ * bounds the number of concurrent requests so that we don't flood the
+ * nameserver. Several algorithms require a full walk of the inflight
+ * queue and so bounding its size keeps thing going nicely under huge
+ * (many thousands of requests) loads.
+ *
+ * If a nameserver loses too many requests it is considered down and we
+ * try not to use it. After a while we send a probe to that nameserver
+ * (a lookup for google.com) and, if it replies, we consider it working
+ * again. If the nameserver fails a probe we wait longer to try again
+ * with the next probe.
+ */
+
+#include "eventdns.h"
+#include "eventdns_tor.h"
+//#define NDEBUG
+
+#ifndef DNS_USE_CPU_CLOCK_FOR_ID
+#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
+#ifndef DNS_USE_OPENSSL_FOR_ID
+#error Must configure at least one id generation method.
+#error Please see the documentation.
+#endif
+#endif
+#endif
+
+// #define _POSIX_C_SOURCE 200507
+#define _GNU_SOURCE
+
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#error Multiple id options selected
+#endif
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <time.h>
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+#error Multiple id options selected
+#endif
+#include <openssl/rand.h>
+#endif
+
+#define _FORTIFY_SOURCE 3
+
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <iphlpapi.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#define EVENTDNS_LOG_DEBUG 0
+#define EVENTDNS_LOG_WARN 1
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
+
+#ifndef NDEBUG
+#include <stdio.h>
+#endif
+
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#if 0
+#ifdef __USE_ISOC99B
+// libevent doesn't work without this
+typedef uint8_t u_char;
+typedef unsigned int uint;
+#endif
+#endif
+#include <event.h>
+
+#define u64 uint64_t
+#define u32 uint32_t
+#define u16 uint16_t
+#define u8 uint8_t
+
+#include "eventdns.h"
+
+#define MAX_ADDRS 4 // maximum number of addresses from a single packet
+// which we bother recording
+
+#define TYPE_A 1
+#define TYPE_CNAME 5
+#define TYPE_PTR 12
+#define TYPE_AAAA 28
+
+#define CLASS_INET 1
+
+struct request {
+ u8 *request; // the dns packet data
+ unsigned int request_len;
+ u8 reissue_count;
+ u8 tx_count; // the number of times that this packet has been sent
+ u8 request_type; // TYPE_PTR or TYPE_A
+ void *user_pointer; // the pointer given to us for this request
+ eventdns_callback_type user_callback;
+ struct nameserver *ns; // the server which we last sent it
+
+ // elements used by the searching code
+ int search_index;
+ struct search_state *search_state;
+ char *search_origname; // needs to be free()ed
+ int search_flags;
+
+ // these objects are kept in a circular list
+ struct request *next, *prev;
+
+ struct event timeout_event;
+
+ u16 trans_id; // the transaction id
+ char request_appended; // true if the request pointer is data which follows this struct
+ char transmit_me; // needs to be transmitted
+};
+
+struct reply {
+ u8 type;
+ u8 have_answer;
+ union {
+ struct {
+ u32 addrcount;
+ u32 addresses[MAX_ADDRS];
+ } a;
+ struct {
+ char name[HOST_NAME_MAX];
+ } ptr;
+ } data;
+};
+
+struct nameserver {
+ int socket; // a connected UDP socket
+ u32 address;
+ int failed_times; // number of times which we have given this server a chance
+ int timedout; // number of times in a row a request has timed out
+ struct event event;
+ // these objects are kept in a circular list
+ struct nameserver *next, *prev;
+ struct event timeout_event; // used to keep the timeout for
+ // when we next probe this server.
+ // Valid if state == 0
+ char state; // zero if we think that this server is down
+ char choaked; // true if we have an EAGAIN from this server's socket
+ char write_waiting; // true if we are waiting for EV_WRITE events
+};
+
+static struct request *req_head = NULL, *req_waiting_head = NULL;
+static struct nameserver *server_head = NULL;
+
+// The number of good nameservers that we have
+static int global_good_nameservers = 0;
+
+// inflight requests are contained in the req_head list
+// and are actually going out across the network
+static int global_requests_inflight = 0;
+// requests which aren't inflight are in the waiting list
+// and are counted here
+static int global_requests_waiting = 0;
+
+static int global_max_requests_inflight = 64;
+
+static struct timeval global_timeout = {3, 0}; // 3 seconds
+static u8 global_max_reissues = 1; // a reissue occurs when we get some errors from the server
+static u8 global_max_retransmits = 3; // number of times we'll retransmit a request which timed out
+// number of timeouts in a row before we consider this server to be down
+static int global_max_nameserver_timeout = 3;
+
+// These are the timeout values for nameservers. If we find a nameserver is down
+// we try to probe it at intervals as given below. Values are in seconds.
+static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}};
+static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval);
+
+const char *const eventdns_error_strings[] = {"no error", "The name server was unable to interpret the query", "The name server suffered an internal error", "The requested domain name does not exist", "The name server refused to reply to the request"};
+
+static struct nameserver *nameserver_pick(void);
+static void eventdns_request_insert(struct request *req, struct request **head);
+static void nameserver_ready_callback(int fd, short events, void *arg);
+static int eventdns_transmit(void);
+static int eventdns_request_transmit(struct request *req);
+static void nameserver_send_probe(struct nameserver *const ns);
+static void search_request_finished(struct request *const);
+static int search_try_next(struct request *const req);
+static int search_request_new(int type, const char *const name, int flags, eventdns_callback_type user_callback, void *user_arg);
+static void eventdns_requests_pump_waiting_queue(void);
+static u16 transaction_id_pick(void);
+static struct request *request_new(int type, const char *name, int flags, eventdns_callback_type, void *ptr);
+static void request_submit(struct request *req);
+
+#ifdef MS_WINDOWS
+static int
+last_error(int sock) {
+ int optval, optvallen=sizeof(optval);
+ int err = WSAGetLastError();
+ if (err == WSAEWOULDBLOCK && sock >= 0) {
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval,
+ &optvallen))
+ return err;
+ if (optval)
+ return optval;
+ }
+ return err;
+
+}
+static int
+error_is_eagain(int err) {
+ return err == EAGAIN || err == WSAEWOULDBLOCK;
+}
+static int
+inet_aton(const char *c, struct in_addr *addr) {
+ uint32_t r;
+ if (strcmp(c, "255.255.255.255") == 0) {
+ addr->s_addr = 0xffffffffu;
+ } else {
+ r = inet_addr(c);
+ if (r == INADDR_NONE)
+ return 0;
+ addr->s_addr = r;
+ }
+ return 1;
+}
+#define CLOSE_SOCKET(x) closesocket(x)
+#else
+#define last_error(sock) (errno)
+#define error_is_eagain(err) ((err) == EAGAIN)
+#define CLOSE_SOCKET(x) close(x)
+#endif
+
+#define ISSPACE(c) isspace((int)(unsigned char)(c))
+#define ISDIGIT(c) isdigit((int)(unsigned char)(c))
+
+#ifndef NDEBUG
+static const char *
+debug_ntoa(u32 address) {
+ static char buf[32];
+ u32 a = ntohl(address);
+ sprintf(buf, "%d.%d.%d.%d",
+ (int)(u8)((a>>24)&0xff),
+ (int)(u8)((a>>16)&0xff),
+ (int)(u8)((a>>8 )&0xff),
+ (int)(u8)((a )&0xff));
+ return buf;
+}
+#endif
+
+static eventdns_debug_log_fn_type eventdns_log_fn = NULL;
+
+void
+eventdns_set_log_fn(eventdns_debug_log_fn_type fn) {
+ eventdns_log_fn = fn;
+}
+
+#ifdef __GNUC__
+#define EVENTDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3)))
+#else
+#define EVENTDNS_LOG_CHECK
+#endif
+
+static void _eventdns_log(int warn, const char *fmt, ...) EVENTDNS_LOG_CHECK;
+static void
+_eventdns_log(int warn, const char *fmt, ...) {
+ va_list args;
+ static char buf[512];
+ if (!eventdns_log_fn)
+ return;
+ va_start(args,fmt);
+#ifdef MS_WINDOWS
+ _vsnprintf(buf, sizeof(buf), fmt, args);
+#else
+ vsnprintf(buf, sizeof(buf), fmt, args);
+#endif
+ buf[sizeof(buf)-1] = '\0';
+ eventdns_log_fn(warn, buf);
+ va_end(args);
+}
+
+#define log _eventdns_log
+
+// This walks the list of inflight requests to find the
+// one with a matching transaction id. Returns NULL on
+// failure
+static struct request *
+request_find_from_trans_id(u16 trans_id) {
+ struct request *req = req_head, *const started_at = req_head;
+
+ if (req) {
+ do {
+ if (req->trans_id == trans_id) return req;
+ req = req->next;
+ } while (req != started_at);
+ }
+
+ return NULL;
+}
+
+// a libevent callback function which is called when a nameserver
+// has gone down and we want to test if it has came back to life yet
+static void
+nameserver_prod_callback(int fd, short events, void *arg) {
+ struct nameserver *const ns = (struct nameserver *) arg;
+ (void)fd;
+ (void)events;
+
+ nameserver_send_probe(ns);
+}
+
+// a libevent callback which is called when a nameserver probe (to see if
+// it has come back to life) times out. We increment the count of failed_times
+// and wait longer to send the next probe packet.
+static void
+nameserver_probe_failed(struct nameserver *const ns) {
+ const struct timeval * timeout;
+ (void) evtimer_del(&ns->timeout_event);
+ if (ns->state == 1) {
+ // This can happen if the nameserver acts in a way which makes us mark
+ // it as bad and then starts sending good replies.
+ return;
+ }
+
+ timeout =
+ &global_nameserver_timeouts[MIN(ns->failed_times,
+ global_nameserver_timeouts_length - 1)];
+ ns->failed_times++;
+
+ evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+ if (evtimer_add(&ns->timeout_event, (struct timeval *) timeout) < 0) {
+ log(EVENTDNS_LOG_WARN,
+ "Error from libevent when adding timer event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+}
+
+// called when a nameserver has been deemed to have failed. For example, too
+// many packets have timed out etc
+static void
+nameserver_failed(struct nameserver *const ns, const char *msg) {
+ struct request *req, *started_at;
+ // if this nameserver has already been marked as failed
+ // then don't do anything
+ if (!ns->state) return;
+
+ log(EVENTDNS_LOG_WARN, "Nameserver %s has failed: %s",
+ debug_ntoa(ns->address), msg);
+ global_good_nameservers--;
+ assert(global_good_nameservers >= 0);
+ if (global_good_nameservers == 0) {
+ log(EVENTDNS_LOG_WARN, "All nameservers have failed");
+ }
+
+ ns->state = 0;
+ ns->failed_times = 1;
+
+ evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
+ if (evtimer_add(&ns->timeout_event, (struct timeval *) &global_nameserver_timeouts[0]) < 0) {
+ log(EVENTDNS_LOG_WARN,
+ "Error from libevent when adding timer event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+
+ // walk the list of inflight requests to see if any can be reassigned to
+ // a different server. Requests in the waiting queue don't have a
+ // nameserver assigned yet
+
+ // if we don't have *any* good nameservers then there's no point
+ // trying to reassign requests to one
+ if (!global_good_nameservers) return;
+
+ req = req_head;
+ started_at = req_head;
+ if (req) {
+ do {
+ if (req->tx_count == 0 && req->ns == ns) {
+ // still waiting to go out, can be moved
+ // to another server
+ req->ns = nameserver_pick();
+ }
+ req = req->next;
+ } while (req != started_at);
+ }
+}
+
+static void
+nameserver_up(struct nameserver *const ns) {
+ if (ns->state) return;
+ log(EVENTDNS_LOG_WARN, "Nameserver %s is back up",
+ debug_ntoa(ns->address));
+ evtimer_del(&ns->timeout_event);
+ ns->state = 1;
+ ns->failed_times = 0;
+ global_good_nameservers++;
+}
+
+static void
+request_trans_id_set(struct request *const req, const u16 trans_id) {
+ req->trans_id = trans_id;
+ *((u16 *) req->request) = htons(trans_id);
+}
+
+// Called to remove a request from a list and dealloc it.
+// head is a pointer to the head of the list it should be
+// removed from or NULL if the request isn't in a list.
+static void
+request_finished(struct request *const req, struct request **head) {
+ if (head) {
+ if (req->next == req) {
+ // only item in the list
+ *head = NULL;
+ } else {
+ req->next->prev = req->prev;
+ req->prev->next = req->next;
+ if (*head == req) *head = req->next;
+ }
+ }
+
+ log(EVENTDNS_LOG_DEBUG, "Removing timeout for request %lx",
+ (unsigned long) req);
+ evtimer_del(&req->timeout_event);
+
+ search_request_finished(req);
+ global_requests_inflight--;
+
+ if (!req->request_appended) {
+ // need to free the request data on it's own
+ free(req->request);
+ } else {
+ // the request data is appended onto the header
+ // so everything gets free()ed when we:
+ }
+
+ free(req);
+
+ eventdns_requests_pump_waiting_queue();
+}
+
+// This is called when a server returns a funny error code.
+// We try the request again with another server.
+//
+// return:
+// 0 ok
+// 1 failed/reissue is pointless
+static int
+request_reissue(struct request *req) {
+ const struct nameserver *const last_ns = req->ns;
+ // the last nameserver should have been marked as failing
+ // by the caller of this function, therefore pick will try
+ // not to return it
+ req->ns = nameserver_pick();
+ if (req->ns == last_ns) {
+ // ... but pick did return it
+ // not a lot of point in trying again with the
+ // same server
+ return 1;
+ }
+
+ req->reissue_count++;
+ req->tx_count = 0;
+ req->transmit_me = 1;
+
+ return 0;
+}
+
+// this function looks for space on the inflight queue and promotes
+// requests from the waiting queue if it can.
+static void
+eventdns_requests_pump_waiting_queue(void) {
+ while (global_requests_inflight < global_max_requests_inflight &&
+ global_requests_waiting) {
+ struct request *req;
+ // move a request from the waiting queue to the inflight queue
+ assert(req_waiting_head);
+ if (req_waiting_head->next == req_waiting_head) {
+ // only one item in the queue
+ req = req_waiting_head;
+ req_waiting_head = NULL;
+ } else {
+ req = req_waiting_head;
+ req->next->prev = req->prev;
+ req->prev->next = req->next;
+ req_waiting_head = req->next;
+ }
+
+ global_requests_waiting--;
+ global_requests_inflight++;
+
+ req->ns = nameserver_pick();
+ request_trans_id_set(req, transaction_id_pick());
+
+ eventdns_request_insert(req, &req_head);
+ eventdns_request_transmit(req);
+ eventdns_transmit();
+ }
+}
+
+static void
+reply_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) {
+ switch (req->request_type) {
+ case TYPE_A:
+ if (reply)
+ req->user_callback(DNS_ERR_NONE, DNS_IPv4_A,
+ reply->data.a.addrcount, ttl,
+ reply->data.a.addresses,
+ req->user_pointer);
+ else
+ req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
+ return;
+ case TYPE_PTR:
+ if (reply) {
+ char *name = reply->data.ptr.name;
+ req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl,
+ &name, req->user_pointer);
+ } else {
+ req->user_callback(err, 0, 0, 0, NULL,
+ req->user_pointer);
+ }
+ return;
+ }
+ assert(0);
+}
+
+// this processes a parsed reply packet
+static void
+reply_handle(struct request *const req,
+ u16 flags, u32 ttl, struct reply *reply) {
+ int error;
+ static const int error_codes[] = {DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED};
+
+ if (flags & 0x020f || !reply || !reply->have_answer) {
+ // there was an error
+ if (flags & 0x0200) {
+ error = DNS_ERR_TRUNCATED;
+ } else {
+ u16 error_code = (flags & 0x000f) - 1;
+ if (error_code > 4) {
+ error = DNS_ERR_UNKNOWN;
+ } else {
+ error = error_codes[error_code];
+ }
+ }
+
+ switch(error) {
+ case DNS_ERR_SERVERFAILED:
+ case DNS_ERR_NOTIMPL:
+ case DNS_ERR_REFUSED:
+ // we regard these errors as marking a bad nameserver
+ if (req->reissue_count < global_max_reissues) {
+ char msg[64];
+ snprintf(msg, sizeof(msg), "Bad response %d",
+ error);
+ nameserver_failed(req->ns, msg);
+ if (!request_reissue(req)) return;
+ }
+ break;
+ default:
+ // we got a good reply from the nameserver
+ nameserver_up(req->ns);
+ }
+
+ if (req->search_state && req->request_type != TYPE_PTR) {
+ // if we have a list of domains to search in, try the next one
+ if (!search_try_next(req)) {
+ // a new request was issued so this request is finished and
+ // the user callback will be made when that request (or a
+ // child of it) finishes.
+ request_finished(req, &req_head);
+ return;
+ }
+ }
+
+ // all else failed. Pass the failure up
+ reply_callback(req, 0, error, NULL);
+ request_finished(req, &req_head);
+ } else {
+ // all ok, tell the user
+ reply_callback(req, ttl, 0, reply);
+ nameserver_up(req->ns);
+ request_finished(req, &req_head);
+ }
+}
+
+static inline int
+name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) {
+ int name_end = -1;
+ int j = *idx;
+#define GET32(x) do { if (j + 4 > length) return -1; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0);
+#define GET16(x) do { if (j + 2 > length) return -1; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0);
+#define GET8(x) do { if (j >= length) return -1; x = packet[j++]; } while(0);
+
+ char *cp = name_out;
+ const char *const end = name_out + name_out_len;
+
+ // Normally, names are a series of length prefixed strings terminated
+ // with a length of 0 (the lengths are u8's < 63).
+ // However, the length can start with a pair of 1 bits and that
+ // means that the next 14 bits are a pointer within the current
+ // packet.
+
+ for(;;) {
+ u8 label_len;
+ if (j >= length) return -1;
+ GET8(label_len);
+ if (!label_len) break;
+ if (label_len & 0xc0) {
+ u8 ptr_low;
+ GET8(ptr_low);
+ if (name_end < 0) name_end = j;
+ j = (((int)label_len & 0x3f) << 8) + ptr_low;
+ if (j < 0 || j >= length) return -1;
+ continue;
+ }
+ if (label_len > 63) return -1;
+ if (cp != name_out) {
+ if (cp + 1 >= end) return -1;
+ *cp++ = '.';
+ }
+ if (cp + label_len >= end) return -1;
+ memcpy(cp, packet + j, label_len);
+ cp += label_len;
+ j += label_len;
+ }
+ if (cp >= end) return -1;
+ *cp = '\0';
+ if (name_end < 0)
+ *idx = j;
+ else
+ *idx = name_end;
+ return 0;
+}
+
+// parses a raw packet from the wire
+static int
+reply_parse(u8 *packet, int length) {
+ int j = 0; // index into packet
+ u16 _t; // used by the macros
+ u32 _t32; // used by the macros
+ char tmp_name[256]; // used by the macros
+
+ u16 trans_id, flags, questions, answers, authority, additional, datalength;
+ u32 ttl, ttl_r = 0xffffffff;
+ struct reply reply;
+ struct request *req;
+ unsigned int i;
+
+ GET16(trans_id);
+ GET16(flags);
+ GET16(questions);
+ GET16(answers);
+ GET16(authority);
+ GET16(additional);
+
+ req = request_find_from_trans_id(trans_id);
+ if (!req) return -1;
+ // XXXX should the other return points also call reply_handle? -NM
+ // log("reqparse: trans was %d\n", (int)trans_id);
+
+ memset(&reply, 0, sizeof(reply));
+
+ if (!(flags & 0x8000)) return -1; // must be an answer
+ if (flags & 0x020f) {
+ // there was an error
+ reply_handle(req, flags, 0, NULL);
+ return -1;
+ }
+ // if (!answers) return; // must have an answer of some form
+
+ // This macro skips a name in the DNS reply.
+#define SKIP_NAME \
+ do { tmp_name[0] = '\0'; \
+ if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) \
+ return -1; \
+ } while(0);
+
+ reply.type = req->request_type;
+
+ // skip over each question in the reply
+ for (i = 0; i < questions; ++i) {
+ // the question looks like
+ // <label:name><u16:type><u16:class>
+ SKIP_NAME;
+ j += 4;
+ if (j >= length) return -1;
+ }
+
+ // now we have the answer section which looks like
+ // <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
+ for (i = 0; i < answers; ++i) {
+ u16 type, class;
+ //int pre = j;
+
+ // XXX I'd be more comfortable if we actually checked the name
+ // here. -NM
+ SKIP_NAME;
+ GET16(type);
+ GET16(class);
+ GET32(ttl);
+ GET16(datalength);
+
+ // log("@%d, Name %s, type %d, class %d, j=%d", pre, tmp_name, (int)type, (int)class, j);
+
+ if (type == TYPE_A && class == CLASS_INET) {
+ int addrcount, addrtocopy;
+ if (req->request_type != TYPE_A) {
+ j += datalength; continue;
+ }
+ // XXXX do something sane with malformed A answers.
+ addrcount = datalength >> 2; // each IP address is 4 bytes
+ addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount);
+ ttl_r = MIN(ttl_r, ttl);
+ // we only bother with the first four addresses.
+ if (j + 4*addrtocopy > length) return -1;
+ memcpy(&reply.data.a.addresses[reply.data.a.addrcount],
+ packet + j, 4*addrtocopy);
+ j += 4*addrtocopy;
+ reply.data.a.addrcount += addrtocopy;
+ reply.have_answer = 1;
+ if (reply.data.a.addrcount == MAX_ADDRS) break;
+ } else if (type == TYPE_PTR && class == CLASS_INET) {
+ if (req->request_type != TYPE_PTR) {
+ j += datalength; continue;
+ }
+ if (name_parse(packet, length, &j, reply.data.ptr.name,
+ sizeof(reply.data.ptr.name))<0)
+ return -1;
+ reply.have_answer = 1;
+ break;
+ } else if (type == TYPE_AAAA && class == CLASS_INET) {
+ if (req->request_type != TYPE_AAAA) {
+ j += datalength; continue;
+ }
+ // XXXX Implement me. -NM
+ j += datalength;
+ } else {
+ // skip over any other type of resource
+ j += datalength;
+ }
+ }
+
+ reply_handle(req, flags, ttl_r, &reply);
+ return 0;
+#undef SKIP_NAME
+#undef GET32
+#undef GET16
+#undef GET8
+}
+
+// Try to choose a strong transaction id which isn't already in flight
+static u16
+transaction_id_pick(void) {
+ for (;;) {
+ const struct request *req = req_head, *started_at;
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+ struct timespec ts;
+ const u16 trans_id = ts.tv_nsec & 0xffff;
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts)) abort();
+#endif
+
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+ struct timeval tv;
+ const u16 trans_id = tv.tv_usec & 0xffff;
+ gettimeofday(&tv, NULL);
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+ u16 trans_id;
+ if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
+ /* // in the case that the RAND call fails we back
+ // down to using gettimeofday.
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ trans_id = tv.tv_usec & 0xffff; */
+ abort();
+ }
+#endif
+
+ if (trans_id == 0xffff) continue;
+ // now check to see if that id is already inflight
+ req = started_at = req_head;
+ if (req) {
+ do {
+ if (req->trans_id == trans_id) break;
+ req = req->next;
+ } while (req != started_at);
+ }
+ // we didn't find it, so this is a good id
+ if (req == started_at) return trans_id;
+ }
+}
+
+// choose a namesever to use. This function will try to ignore
+// nameservers which we think are down and load balance across the rest
+// by updating the server_head global each time.
+static struct nameserver *
+nameserver_pick(void) {
+ struct nameserver *started_at = server_head, *picked;
+ if (!server_head) return NULL;
+
+ // if we don't have any good nameservers then there's no
+ // point in trying to find one.
+ if (!global_good_nameservers) {
+ server_head = server_head->next;
+ return server_head;
+ }
+
+ // remember that nameservers are in a circular list
+ for (;;) {
+ if (server_head->state) {
+ // we think this server is currently good
+ picked = server_head;
+ server_head = server_head->next;
+ return picked;
+ }
+
+ server_head = server_head->next;
+ if (server_head == started_at) {
+ // all the nameservers seem to be down
+ // so we just return this one and hope for the
+ // best
+ assert(global_good_nameservers == 0);
+ picked = server_head;
+ server_head = server_head->next;
+ return picked;
+ }
+ }
+}
+
+// this is called when a namesever socket is ready for reading
+static void
+nameserver_read(struct nameserver *ns) {
+ u8 packet[1500];
+
+ for (;;) {
+ const int r = recv(ns->socket, packet, sizeof(packet), 0);
+ if (r < 0) {
+ int err = last_error(ns->socket);
+ if (error_is_eagain(err)) return;
+ nameserver_failed(ns, strerror(err));
+ return;
+ }
+ reply_parse(packet, r);
+ }
+}
+
+// set if we are waiting for the ability to write to this server.
+// if waiting is true then we ask libevent for EV_WRITE events, otherwise
+// we stop these events.
+static void
+nameserver_write_waiting(struct nameserver *ns, char waiting) {
+ if (ns->write_waiting == waiting) return;
+
+ ns->write_waiting = waiting;
+ (void) event_del(&ns->event);
+ event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST,
+ nameserver_ready_callback, ns);
+ if (event_add(&ns->event, NULL) < 0) {
+ log(EVENTDNS_LOG_WARN, "Error from libevent when adding event for %s",
+ debug_ntoa(ns->address));
+ // ???? Do more?
+ }
+}
+
+// a callback function. Called by libevent when the kernel says that
+// a nameserver socket is ready for writing or reading
+static void
+nameserver_ready_callback(int fd, short events, void *arg) {
+ struct nameserver *ns = (struct nameserver *) arg;
+ (void)fd;
+
+ if (events & EV_WRITE) {
+ ns->choaked = 0;
+ if (!eventdns_transmit()) {
+ nameserver_write_waiting(ns, 0);
+ }
+ }
+ if (events & EV_READ) {
+ nameserver_read(ns);
+ }
+}
+
+// Converts a string to a length-prefixed set of DNS labels.
+// @buf must be strlen(name)+2 or longer. name and buf must
+// not overlap. name_len should be the length of name
+//
+// Input: abc.def
+// Output: <3>abc<3>def<0>
+//
+// Returns the length of the data. negative on error
+// -1 label was > 63 bytes
+// -2 name was > 255 bytes
+static int
+dnsname_to_labels(u8 *const buf, const char *name, const int name_len) {
+ const char *end = name + name_len;
+ int j = 0; // current offset into buf
+
+ if (name_len > 255) return -2;
+
+ for (;;) {
+ const char *const start = name;
+ name = strchr(name, '.');
+ if (!name) {
+ const unsigned int label_len = end - start;
+ if (label_len > 63) return -1;
+ buf[j++] = label_len;
+
+ memcpy(buf + j, start, end - start);
+ j += end - start;
+ break;
+ } else {
+ // append length of the label.
+ const unsigned int label_len = name - start;
+ if (label_len > 63) return -1;
+ buf[j++] = label_len;
+
+ memcpy(buf + j, start, name - start);
+ j += name - start;
+ // hop over the '.'
+ name++;
+ }
+ }
+
+ // the labels must be terminated by a 0.
+ // It's possible that the name ended in a .
+ // in which case the zero is already there
+ if (!j || buf[j-1]) buf[j++] = 0;
+ return j;
+}
+
+// Finds the length of a dns request for a DNS name of the given
+// length. The actual request may be smaller than the value returned
+// here
+static int
+eventdns_request_len(const int name_len) {
+ return 96 + // length of the DNS standard header
+ name_len + 2 +
+ 4; // space for the resource type
+}
+
+// build a dns request packet into buf. buf should be at least as long
+// as eventdns_request_len told you it should be.
+//
+// Returns the amount of space used. Negative on error.
+static int
+eventdns_request_data_build(const char *const name, const int name_len, const u16 trans_id,
+ const u16 type, const u16 class,
+ u8 *const buf) {
+ int j = 0; // current offset into buf
+ u16 _t; // used by the macros
+ u8 *labels;
+ int labels_len;
+
+#define APPEND16(x) do { _t = htons(x); memcpy(buf + j, &_t, 2); j += 2; } while(0);
+ APPEND16(trans_id);
+ APPEND16(0x0100); // standard query, recusion needed
+ APPEND16(1); // one question
+ APPEND16(0); // no answers
+ APPEND16(0); // no authority
+ APPEND16(0); // no additional
+
+ labels = (u8 *) malloc(name_len + 2);
+ if (!labels) return -1;
+ labels_len = dnsname_to_labels(labels, name, name_len);
+ if (labels_len < 0) {
+ free(labels);
+ return labels_len;
+ }
+ memcpy(buf + j, labels, labels_len);
+ j += labels_len;
+ free(labels);
+
+ APPEND16(type);
+ APPEND16(class);
+#undef APPEND16
+
+ return j;
+}
+
+// this is a libevent callback function which is called when a request
+// has timed out.
+static void
+eventdns_request_timeout_callback(int fd, short events, void *arg) {
+ struct request *const req = (struct request *) arg;
+ (void) fd;
+ (void) events;
+
+ log(EVENTDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg);
+
+ req->ns->timedout++;
+ if (req->ns->timedout > global_max_nameserver_timeout) {
+ nameserver_failed(req->ns, "request timed out.");
+ }
+
+ (void) evtimer_del(&req->timeout_event);
+ if (req->tx_count >= global_max_retransmits) {
+ // this request has failed
+ reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
+ request_finished(req, &req_head);
+ } else {
+ // retransmit it
+ eventdns_request_transmit(req);
+ }
+}
+
+// try to send a request to a given server.
+//
+// return:
+// 0 ok
+// 1 temporary failure
+// 2 other failure
+static int
+eventdns_request_transmit_to(struct request *req, struct nameserver *server) {
+ const int r = send(server->socket, req->request, req->request_len, 0);
+ if (r < 0) {
+ int err = last_error(server->socket);
+ if (error_is_eagain(err)) return 1;
+ nameserver_failed(req->ns, strerror(err));
+ return 2;
+ } else if (r != (int)req->request_len) {
+ return 1; // short write
+ } else {
+ return 0;
+ }
+}
+
+// try to send a request, updating the fields of the request
+// as needed
+//
+// return:
+// 0 ok
+// 1 failed
+static int
+eventdns_request_transmit(struct request *req) {
+ int retcode = 0, r;
+
+ // if we fail to send this packet then this flag marks it
+ // for eventdns_transmit
+ req->transmit_me = 1;
+ if (req->trans_id == 0xffff) abort();
+
+ if (req->ns->choaked) {
+ // don't bother trying to write to a socket
+ // which we have had EAGAIN from
+ return 1;
+ }
+
+ r = eventdns_request_transmit_to(req, req->ns);
+ switch (r) {
+ case 1:
+ // temp failure
+ req->ns->choaked = 1;
+ nameserver_write_waiting(req->ns, 1);
+ return 1;
+ case 2:
+ // failed in some other way
+ retcode = 1;
+ // fall through
+ default:
+ // all ok
+ log(EVENTDNS_LOG_DEBUG,
+ "Setting timeout for request %lx", (unsigned long) req);
+ evtimer_set(&req->timeout_event, eventdns_request_timeout_callback, req);
+ if (evtimer_add(&req->timeout_event, &global_timeout) < 0) {
+ log(EVENTDNS_LOG_WARN,
+ "Error from libevent when adding timer for "
+ "request %lx", (unsigned long) req);
+ // ???? Do more?
+ }
+ req->tx_count++;
+ req->transmit_me = 0;
+ return retcode;
+ }
+}
+
+static void
+nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) {
+ struct nameserver *const ns = (struct nameserver *) arg;
+ (void) type;
+ (void) count;
+ (void) ttl;
+ (void) addresses;
+
+ if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) {
+ // this is a good reply
+ nameserver_up(ns);
+ } else nameserver_probe_failed(ns);
+}
+
+static void
+nameserver_send_probe(struct nameserver *const ns) {
+ struct request *req;
+ // here we need to send a probe to a given nameserver
+ // in the hope that it is up now.
+
+ log(EVENTDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address));
+ req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns);
+ if (!req) return;
+ // we force this into the inflight queue no matter what
+ request_trans_id_set(req, transaction_id_pick());
+ req->ns = ns;
+ request_submit(req);
+}
+
+// returns:
+// 0 didn't try to transmit anything
+// 1 tried to transmit something
+static int
+eventdns_transmit(void) {
+ char did_try_to_transmit = 0;
+
+ if (req_head) {
+ struct request *const started_at = req_head, *req = req_head;
+ // first transmit all the requests which are currently waiting
+ do {
+ if (req->transmit_me) {
+ did_try_to_transmit = 1;
+ eventdns_request_transmit(req);
+ }
+
+ req = req->next;
+ } while (req != started_at);
+ }
+
+ return did_try_to_transmit;
+}
+
+// exported function
+int
+eventdns_count_nameservers(void) {
+ const struct nameserver *server = server_head;
+ int n = 0;
+ if (!server)
+ return 0;
+ do {
+ ++n;
+ server = server->next;
+ } while (server != server_head);
+ return n;
+}
+
+// exported function
+int
+eventdns_clear_nameservers_and_suspend(void) {
+ struct nameserver *server = server_head, *started_at = server_head;
+ struct request *req = req_head, *req_started_at = req_head;
+
+ if (!server)
+ return 0;
+ while (1) {
+ struct nameserver *next = server->next;
+ (void) event_del(&server->event);
+ (void) evtimer_del(&server->timeout_event);
+ if (server->socket >= 0)
+ CLOSE_SOCKET(server->socket);
+ free(server);
+ if (next == started_at)
+ break;
+ server = next;
+ }
+ server_head = NULL;
+ global_good_nameservers = 0;
+
+ while (req) {
+ struct request *next = req->next;
+ req->tx_count = req->reissue_count = 0;
+ req->ns = NULL;
+ // ???? What to do about searches?
+ (void) evtimer_del(&req->timeout_event);
+ req->trans_id = 0;
+ req->transmit_me = 0;
+
+ global_requests_waiting++;
+ eventdns_request_insert(req, &req_waiting_head);
+ /* We want to insert these suspended elements at the front of
+ * the waiting queue, since they were pending before any of
+ * the waiting entries were added. This is a circular list,
+ * so we can just shift the start back by one.*/
+ req_waiting_head = req_waiting_head->prev;
+
+ if (next == req_started_at)
+ break;
+ req = next;
+ }
+ req_head = NULL;
+ global_requests_inflight = 0;
+
+ return 0;
+}
+
+// exported function
+int
+eventdns_resume(void) {
+ eventdns_requests_pump_waiting_queue();
+ return 0;
+}
+
+// exported function
+int
+eventdns_nameserver_add(unsigned long int address) {
+ // first check to see if we already have this nameserver
+
+ const struct nameserver *server = server_head, *const started_at = server_head;
+ struct nameserver *ns;
+ struct sockaddr_in sin;
+ int err = 0;
+ if (server) {
+ do {
+ if (server->address == address) return 3;
+ server = server->next;
+ } while (server != started_at);
+ }
+
+ ns = (struct nameserver *) malloc(sizeof(struct nameserver));
+ if (!ns) return -1;
+
+ memset(ns, 0, sizeof(struct nameserver));
+
+ ns->socket = socket(PF_INET, SOCK_DGRAM, 0);
+ if (ns->socket < 0) { err = 1; goto out1; }
+#ifdef MS_WINDOWS
+ {
+ u_long nonblocking = 1;
+ ioctlsocket(ns->socket, FIONBIO, &nonblocking);
+ }
+#else
+ fcntl(ns->socket, F_SETFL, O_NONBLOCK);
+#endif
+ sin.sin_addr.s_addr = address;
+ sin.sin_port = htons(53);
+ sin.sin_family = AF_INET;
+ if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
+ err = 2;
+ goto out2;
+ }
+
+ ns->address = address;
+ ns->state = 1;
+ event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
+ if (event_add(&ns->event, NULL) < 0) {
+ err = 2;
+ goto out2;
+ }
+
+ log(EVENTDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address));
+
+ // insert this nameserver into the list of them
+ if (!server_head) {
+ ns->next = ns->prev = ns;
+ server_head = ns;
+ } else {
+ ns->next = server_head->next;
+ ns->prev = server_head;
+ server_head->next = ns;
+ if (server_head->prev == server_head) {
+ server_head->prev = ns;
+ }
+ }
+
+ global_good_nameservers++;
+
+ return 0;
+
+ out2:
+ CLOSE_SOCKET(ns->socket);
+ out1:
+ free(ns);
+ log(EVENTDNS_LOG_WARN, "Unable to add nameserver %s: error %d",
+ debug_ntoa(address), err);
+ return err;
+}
+
+// exported function
+int
+eventdns_nameserver_ip_add(const char *ip_as_string) {
+ struct in_addr ina;
+ if (!inet_aton(ip_as_string, &ina)) return 4;
+ return eventdns_nameserver_add(ina.s_addr);
+}
+
+// insert into the tail of the queue
+static void
+eventdns_request_insert(struct request *req, struct request **head) {
+ if (!*head) {
+ *head = req;
+ req->next = req->prev = req;
+ return;
+ }
+
+ req->prev = (*head)->prev;
+ req->prev->next = req;
+ req->next = *head;
+ (*head)->prev = req;
+}
+
+static int
+string_num_dots(const char *s) {
+ int count = 0;
+ while ((s = strchr(s, '.'))) {
+ s++;
+ count++;
+ }
+ return count;
+}
+
+static struct request *
+request_new(int type, const char *name, int flags, eventdns_callback_type callback, void *user_ptr) {
+ const char issuing_now = (global_requests_inflight < global_max_requests_inflight) ? 1 : 0;
+
+ const int name_len = strlen(name);
+ const int request_max_len = eventdns_request_len(name_len);
+ const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff;
+ // the request data is alloced in a single block with the header
+ struct request *const req = (struct request *) malloc(sizeof(struct request) + request_max_len);
+ int rlen;
+ (void) flags;
+
+ if (!req) return NULL;
+ memset(req, 0, sizeof(struct request));
+
+ // request data lives just after the header
+ req->request = ((u8 *) req) + sizeof(struct request);
+ req->request_appended = 1; // denotes that the request data shouldn't be free()ed
+ rlen = eventdns_request_data_build(name, name_len, trans_id, type, CLASS_INET, req->request);
+ if (rlen < 0) goto err1;
+ req->request_len = rlen;
+ req->trans_id = trans_id;
+ req->tx_count = 0;
+ req->request_type = type;
+ req->user_pointer = user_ptr;
+ req->user_callback = callback;
+ req->ns = issuing_now ? nameserver_pick() : NULL;
+ req->next = req->prev = NULL;
+
+ return req;
+ err1:
+ free(req);
+ return NULL;
+}
+
+static void
+request_submit(struct request *const req) {
+ if (req->ns) {
+ // if it has a nameserver assigned then this is going
+ // straight into the inflight queue
+ eventdns_request_insert(req, &req_head);
+ global_requests_inflight++;
+ eventdns_request_transmit(req);
+ } else {
+ eventdns_request_insert(req, &req_waiting_head);
+ global_requests_waiting++;
+ }
+}
+
+// exported function
+int eventdns_resolve_ipv4(const char *name, int flags, eventdns_callback_type callback, void *ptr) {
+ log(EVENTDNS_LOG_DEBUG, "Resolve requested for %s", name);
+ if (flags & DNS_QUERY_NO_SEARCH) {
+ struct request *const req = request_new(TYPE_A, name, flags, callback, ptr);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+ } else {
+ return search_request_new(TYPE_A, name, flags, callback, ptr);
+ }
+}
+
+int eventdns_resolve_reverse(struct in_addr *in, int flags, eventdns_callback_type callback, void *ptr) {
+ char buf[32];
+ struct request *req;
+ u32 a;
+ assert(in);
+ a = ntohl(in->s_addr);
+ sprintf(buf, "%d.%d.%d.%d.in-addr.arpa",
+ (int)(u8)((a )&0xff),
+ (int)(u8)((a>>8 )&0xff),
+ (int)(u8)((a>>16)&0xff),
+ (int)(u8)((a>>24)&0xff));
+ log(EVENTDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
+ req = request_new(TYPE_PTR, buf, flags, callback, ptr);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// Search support
+//
+// the libc resolver has support for searching a number of domains
+// to find a name. If nothing else then it takes the single domain
+// from the gethostname() call.
+//
+// It can also be configured via the domain and search options in a
+// resolv.conf.
+//
+// The ndots option controls how many dots it takes for the resolver
+// to decide that a name is non-local and so try a raw lookup first.
+
+struct search_domain {
+ int len;
+ struct search_domain *next;
+ // the text string is appended to this structure
+};
+
+struct search_state {
+ int refcount;
+ int ndots;
+ int num_domains;
+ struct search_domain *head;
+};
+
+static struct search_state *global_search_state = NULL;
+
+static void
+search_state_decref(struct search_state *const state) {
+ if (!state) return;
+ state->refcount--;
+ if (!state->refcount) {
+ struct search_domain *next, *dom;
+ for (dom = state->head; dom; dom = next) {
+ next = dom->next;
+ free(dom);
+ }
+ free(state);
+ }
+}
+
+static struct search_state *
+search_state_new(void) {
+ struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state));
+ if (!state) return NULL;
+ memset(state, 0, sizeof(struct search_state));
+ state->refcount = 1;
+ state->ndots = 1;
+
+ return state;
+}
+
+static void
+search_postfix_clear(void) {
+ search_state_decref(global_search_state);
+
+ global_search_state = search_state_new();
+}
+
+// exported function
+void
+eventdns_search_clear(void) {
+ search_postfix_clear();
+}
+
+static void
+search_postfix_add(const char *domain) {
+ int domain_len;
+ struct search_domain *sdomain;
+ while (domain[0] == '.') domain++;
+ domain_len = strlen(domain);
+
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->num_domains++;
+
+ sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len);
+ if (!sdomain) return;
+ memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len);
+ sdomain->next = global_search_state->head;
+ sdomain->len = domain_len;
+
+ global_search_state->head = sdomain;
+}
+
+// reverse the order of members in the postfix list. This is needed because,
+// when parsing resolv.conf we push elements in the wrong order
+static void
+search_reverse(void) {
+ struct search_domain *cur, *prev = NULL, *next;
+ cur = global_search_state->head;
+ while (cur) {
+ next = cur->next;
+ cur->next = prev;
+ prev = cur;
+ cur = next;
+ }
+
+ global_search_state->head = prev;
+}
+
+// exported function
+void
+eventdns_search_add(const char *domain) {
+ search_postfix_add(domain);
+}
+
+// exported function
+void
+eventdns_search_ndots_set(const int ndots) {
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->ndots = ndots;
+}
+
+static void
+search_set_from_hostname(void) {
+ char hostname[HOST_NAME_MAX + 1], *domainname;
+
+ search_postfix_clear();
+ if (gethostname(hostname, sizeof(hostname))) return;
+ domainname = strchr(hostname, '.');
+ if (!domainname) return;
+ search_postfix_add(domainname);
+}
+
+// warning: returns malloced string
+static char *
+search_make_new(const struct search_state *const state, int n, const char *const base_name) {
+ const int base_len = strlen(base_name);
+ const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
+ struct search_domain *dom;
+
+ for (dom = state->head; dom; dom = dom->next) {
+ if (!n--) {
+ // this is the postfix we want
+ // the actual postfix string is kept at the end of the structure
+ const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain);
+ const int postfix_len = dom->len;
+ char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1);
+ if (!newname) return NULL;
+ memcpy(newname, base_name, base_len);
+ if (need_to_append_dot) newname[base_len] = '.';
+ memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len);
+ newname[base_len + need_to_append_dot + postfix_len] = 0;
+ return newname;
+ }
+ }
+
+ // we ran off the end of the list and still didn't find the requested string
+ abort();
+}
+
+static int
+search_request_new(int type, const char *const name, int flags, eventdns_callback_type user_callback, void *user_arg) {
+ assert(type == TYPE_A);
+ if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) &&
+ global_search_state &&
+ global_search_state->num_domains) {
+ // we have some domains to search
+ struct request *req;
+ if (string_num_dots(name) >= global_search_state->ndots) {
+ req = request_new(type, name, flags, user_callback, user_arg);
+ if (!req) return 1;
+ req->search_index = -1;
+ } else {
+ char *const new_name = search_make_new(global_search_state, 0, name);
+ if (!new_name) return 1;
+ req = request_new(type, new_name, flags, user_callback, user_arg);
+ free(new_name);
+ if (!req) return 1;
+ req->search_index = 0;
+ }
+ req->search_origname = strdup(name);
+ req->search_state = global_search_state;
+ req->search_flags = flags;
+ global_search_state->refcount++;
+ request_submit(req);
+ return 0;
+ } else {
+ struct request *const req = request_new(type, name, flags, user_callback, user_arg);
+ if (!req) return 1;
+ request_submit(req);
+ return 0;
+ }
+}
+
+// this is called when a request has failed to find a name. We need to check
+// if it is part of a search and, if so, try the next name in the list
+// returns:
+// 0 another request has been submitted
+// 1 no more requests needed
+static int
+search_try_next(struct request *const req) {
+ if (req->search_state) {
+ // it is part of a search
+ char *new_name;
+ struct request *newreq;
+ req->search_index++;
+ if (req->search_index >= req->search_state->num_domains) {
+ // no more postfixes to try, however we may need to try
+ // this name without a postfix
+ if (string_num_dots(req->search_origname) < req->search_state->ndots) {
+ // yep, we need to try it raw
+ struct request *const newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer);
+ log(EVENTDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname);
+ if (newreq) {
+ request_submit(newreq);
+ return 0;
+ }
+ }
+ return 1;
+ }
+
+ new_name = search_make_new(req->search_state, req->search_index, req->search_origname);
+ if (!new_name) return 1;
+ log(EVENTDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index);
+ newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer);
+ free(new_name);
+ if (!newreq) return 1;
+ newreq->search_origname = req->search_origname;
+ req->search_origname = NULL;
+ newreq->search_state = req->search_state;
+ newreq->search_flags = req->search_flags;
+ newreq->search_index = req->search_index;
+ newreq->search_state->refcount++;
+ request_submit(newreq);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+search_request_finished(struct request *const req) {
+ if (req->search_state) {
+ search_state_decref(req->search_state);
+ req->search_state = NULL;
+ }
+ if (req->search_origname) {
+ free(req->search_origname);
+ req->search_origname = NULL;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Parsing resolv.conf files
+
+static void
+eventdns_resolv_set_defaults(int flags) {
+ // if the file isn't found then we assume a local resolver
+ if (flags & DNS_OPTION_SEARCH) search_set_from_hostname();
+ if (flags & DNS_OPTION_NAMESERVERS) eventdns_nameserver_ip_add("127.0.0.1");
+}
+
+#ifndef HAVE_STRTOK_R
+static char *
+strtok_r(char *s, const char *delim, char **state) {
+ return strtok(s, delim);
+}
+#endif
+
+// helper version of atoi which returns -1 on error
+static int
+strtoint(const char *const str) {
+ char *endptr;
+ const int r = strtol(str, &endptr, 10);
+ if (*endptr) return -1;
+ return r;
+}
+
+static void
+resolv_conf_parse_line(char *const start, int flags) {
+ char *strtok_state;
+ static const char *const delims = " \t";
+#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state)
+
+ char *const first_token = strtok_r(start, delims, &strtok_state);
+ if (!first_token) return;
+
+ if (!strcmp(first_token, "nameserver")) {
+ const char *const nameserver = NEXT_TOKEN;
+ struct in_addr ina;
+
+ if (inet_aton(nameserver, &ina)) {
+ // address is valid
+ eventdns_nameserver_add(ina.s_addr);
+ }
+ } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) {
+ const char *const domain = NEXT_TOKEN;
+ if (domain) {
+ search_postfix_clear();
+ search_postfix_add(domain);
+ }
+ } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) {
+ const char *domain;
+ search_postfix_clear();
+
+ while ((domain = NEXT_TOKEN)) {
+ search_postfix_add(domain);
+ }
+ search_reverse();
+ } else if (!strcmp(first_token, "options")) {
+ const char *option;
+
+ while ((option = NEXT_TOKEN)) {
+ if (!strncmp(option, "ndots:", 6)) {
+ const int ndots = strtoint(&option[6]);
+ if (ndots == -1) continue;
+ if (!(flags & DNS_OPTION_SEARCH)) continue;
+ log(EVENTDNS_LOG_DEBUG,"Setting ndots to %d", ndots);
+ if (!global_search_state) global_search_state = search_state_new();
+ if (!global_search_state) return;
+ global_search_state->ndots = ndots;
+ } else if (!strncmp(option, "timeout:", 8)) {
+ const int timeout = strtoint(&option[8]);
+ if (timeout == -1) continue;
+ if (!(flags & DNS_OPTION_MISC)) continue;
+ log(EVENTDNS_LOG_DEBUG,"Setting timeout to %d", timeout);
+ global_timeout.tv_sec = timeout;
+ } else if (!strncmp(option, "attempts:", 9)) {
+ int retries = strtoint(&option[9]);
+ if (retries == -1) continue;
+ if (retries > 255) retries = 255;
+ if (!(flags & DNS_OPTION_MISC)) continue;
+ log(EVENTDNS_LOG_DEBUG,"Setting retries to %d", retries);
+ global_max_retransmits = retries;
+ }
+ }
+ }
+#undef NEXT_TOKEN
+}
+
+// exported function
+// returns:
+// 0 no errors
+// 1 failed to open file
+// 2 failed to stat file
+// 3 file too large
+// 4 out of memory
+// 5 short read from file
+int
+eventdns_resolv_conf_parse(int flags, const char *const filename) {
+ struct stat st;
+ int fd;
+ u8 *resolv;
+ char *start;
+ int err = 0;
+
+ log(EVENTDNS_LOG_DEBUG,"Parsing resolve.conf file %s", filename);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ eventdns_resolv_set_defaults(flags);
+ return 0;
+ }
+
+ if (fstat(fd, &st)) { err = 2; goto out1; }
+ if (!st.st_size) {
+ eventdns_resolv_set_defaults(flags);
+ err = 0;
+ goto out1;
+ }
+ if (st.st_size > 65535) { err = 3; goto out1; } // no resolv.conf should be any bigger
+
+ resolv = (u8 *) malloc(st.st_size + 1);
+ if (!resolv) { err = 4; goto out1; }
+
+ if (read(fd, resolv, st.st_size) != st.st_size) { err = 5; goto out2; }
+ resolv[st.st_size] = 0; // we malloced an extra byte
+
+ start = (char *) resolv;
+ for (;;) {
+ char *const newline = strchr(start, '\n');
+ if (!newline) {
+ resolv_conf_parse_line(start, flags);
+ break;
+ } else {
+ *newline = 0;
+ resolv_conf_parse_line(start, flags);
+ start = newline + 1;
+ }
+ }
+
+ if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) {
+ // no nameservers were configured.
+ eventdns_nameserver_ip_add("127.0.0.1");
+ }
+ if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) {
+ search_set_from_hostname();
+ }
+
+out2:
+ free(resolv);
+out1:
+ close(fd);
+ return err;
+}
+
+#ifdef MS_WINDOWS
+// Add multiple nameservers from a space-or-comma-separated list.
+static int
+eventdns_nameserver_ip_add_line(const char *ips) {
+ const char *addr;
+ char *buf;
+ int r;
+ while (*ips) {
+ while (ISSPACE(*ips) || *ips == ',' || *ips == '\t')
+ ++ips;
+ addr = ips;
+ while (ISDIGIT(*ips) || *ips == '.')
+ ++ips;
+ buf = malloc(ips-addr+1);
+ if (!buf) return 4;
+ memcpy(buf, addr, ips-addr);
+ buf[ips-addr] = '\0';
+ r = eventdns_nameserver_ip_add(buf);
+ free(buf);
+ if (r) return r;
+ }
+ return 0;
+}
+
+typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*);
+
+// Use the windows GetNetworkParams interface in iphlpapi.dll to
+// figure out what our nameservers are.
+static int
+load_nameservers_with_getnetworkparams(void) {
+ // Based on MSDN examples and inspection of c-ares code.
+ FIXED_INFO *fixed;
+ HMODULE handle = 0;
+ ULONG size = sizeof(FIXED_INFO);
+ void *buf = NULL;
+ int status = 0, r, added_any;
+ IP_ADDR_STRING *ns;
+ GetNetworkParams_fn_t fn;
+
+ if (!(handle = LoadLibrary("iphlpapi.dll"))) {
+ log(EVENTDNS_LOG_WARN,"Could not open iphlpapi.dll");
+ //right now status = 0, doesn't that mean "good" - mikec
+ status = -1;
+ goto done;
+ }
+
+ if (!(fn =
+ (GetNetworkParams_fn_t)
+ GetProcAddress(handle, "GetNetworkParams"))) {
+ log(EVENTDNS_LOG_WARN,"Could not get address of function.");
+ //same as above
+ status = -1;
+ goto done;
+ }
+
+ buf = malloc(size);
+ if (!buf) {
+ status = 4;
+ goto done;
+ }
+ fixed = buf;
+ r = fn(fixed, &size);
+ if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) {
+ status = -1;
+ goto done;
+ }
+ if (r != ERROR_SUCCESS) {
+ free(buf);
+ buf = malloc(size);
+ if (!buf) { status = 4; goto done; }
+ fixed = buf;
+ r = fn(fixed, &size);
+ if (r != ERROR_SUCCESS) {
+ log(EVENTDNS_LOG_DEBUG,"fn() failed.");
+ status = -1;
+ goto done;
+ }
+ }
+
+ assert(fixed);
+ added_any = 0;
+ ns = &(fixed->DnsServerList);
+ while (ns) {
+ r = eventdns_nameserver_ip_add_line(ns->IpAddress.String);
+ if (r) {
+ log(EVENTDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d",
+ (ns->IpAddress.String),(int)GetLastError());
+ status = r;
+ goto done;
+ } else {
+ log(EVENTDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String);
+ }
+
+ added_any++;
+ ns = ns->Next;
+ }
+
+ if (!added_any) {
+ //should we ever get here? - mikec
+ log(EVENTDNS_LOG_DEBUG,"No name servers added.");
+ status = -1;
+ }
+
+ done:
+ if (buf)
+ free(buf);
+ if (handle)
+ FreeLibrary(handle);
+ return status;
+}
+
+static int
+config_nameserver_from_reg_key(HKEY key, const char *subkey) {
+ char *buf;
+ DWORD bufsz = 0, type = 0;
+ int status = 0;
+
+ if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz)
+ != ERROR_MORE_DATA)
+ return -1;
+ if (!(buf = malloc(bufsz)))
+ return -1;
+
+ if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz)
+ == ERROR_SUCCESS && bufsz > 1) {
+ status = eventdns_nameserver_ip_add_line(buf);
+ }
+
+ free(buf);
+ return status;
+}
+
+#define SERVICES_KEY "System\\CurrentControlSet\\Services\\"
+
+#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP"
+#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters"
+
+static int
+load_nameservers_from_registry(void) {
+ int found = 0;
+ int r;
+#define TRY(k, name) \
+ if (!found && config_nameserver_from_reg_key(k,name) == 0) { \
+ log(EVENTDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \
+ found = 1; \
+ } else { \
+ if (!found) \
+ log(EVENTDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \
+ #k,#name); \
+ }
+
+ if (((int)GetVersion()) > 0) { /* NT */
+ HKEY nt_key = 0, interfaces_key = 0;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
+ KEY_READ, &nt_key) != ERROR_SUCCESS) {
+ log(EVENTDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError());
+ return -1;
+ }
+
+ r = RegOpenKeyEx(nt_key, "Interfaces", 0,
+ KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,
+ &interfaces_key);
+
+ if (r != ERROR_SUCCESS ) {
+ log(EVENTDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError());
+ return -1;
+ }
+
+ TRY(nt_key, "NameServer");
+ TRY(nt_key, "DhcpNameServer");
+ TRY(interfaces_key, "NameServer");
+ TRY(interfaces_key, "DhcpNameServer");
+ RegCloseKey(interfaces_key);
+ RegCloseKey(nt_key);
+ } else {
+ HKEY win_key = 0;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0,
+ KEY_READ, &win_key) != ERROR_SUCCESS) {
+ log(EVENTDNS_LOG_DEBUG,"Couldn't open registry key, %d",(int)GetLastError());
+ return -1;
+
+ }
+ TRY(win_key, "NameServer");
+ RegCloseKey(win_key);
+ }
+
+ if (found == 0) {
+ log(EVENTDNS_LOG_WARN,"Didn't find any nameservers.");
+ }
+
+ return found ? 0 : -1;
+#undef TRY
+}
+
+int
+eventdns_config_windows_nameservers(void) {
+ if (load_nameservers_with_getnetworkparams() == 0) {
+ return 0;
+ }
+
+ return load_nameservers_from_registry();
+}
+#endif
+
+#ifdef EVENTDNS_MAIN
+void
+main_callback(int result, char type, int count, int ttl,
+ void *addrs, void *orig) {
+ char *n = (char*)orig;
+ int i;
+ for (i = 0; i < count; ++i) {
+ if (type == DNS_IPv4_A) {
+ printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
+ } else if (type == DNS_PTR) {
+ printf("%s: %s\n", n, ((char**)addrs)[i]);
+ }
+ }
+ if (!count) {
+ printf("%s: No answer (%d)\n", n, result);
+ }
+ fflush(stdout);
+}
+
+void
+logfn(const char *msg) {
+ fprintf(stderr, "%s\n", msg);
+}
+int
+main(int c, char **v) {
+ int idx;
+ int reverse = 0, verbose = 1;
+ if (c<2) {
+ fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
+ return 1;
+ }
+ idx = 1;
+ while (idx < c && v[idx][0] == '-') {
+ if (!strcmp(v[idx], "-x"))
+ reverse = 1;
+ else if (!strcmp(v[idx], "-v"))
+ verbose = 1;
+ else
+ fprintf(stderr, "Unknown option %s\n", v[idx]);
+ ++idx;
+ }
+ event_init();
+ if (verbose)
+ eventdns_set_log_fn(logfn);
+ eventdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
+ for (; idx < c; ++idx) {
+ if (reverse) {
+ struct in_addr addr;
+ if (!inet_aton(v[idx], &addr)) {
+ fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
+ continue;
+ }
+ fprintf(stderr, "resolving %s...\n",v[idx]);
+ eventdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
+ } else {
+ fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
+ eventdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
+ }
+ }
+ fflush(stdout);
+ event_dispatch();
+ return 0;
+}
+
+#endif
+
+// Local Variables:
+// tab-width: 4
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// End:
+
diff --git a/src/or/eventdns.h b/src/or/eventdns.h
new file mode 100644
index 0000000000..0897b50ca6
--- /dev/null
+++ b/src/or/eventdns.h
@@ -0,0 +1,73 @@
+/* This software is Public Domain. To view a copy of the public domain dedication,
+ * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ *
+ * I ask and expect, but do not require, that all derivative works contain an
+ * attribution similar to:
+ * Parts developed by Adam Langley <agl@imperialviolet.org>
+ *
+ * You may wish to replace the word "Parts" with something else depending on
+ * the amount of original code.
+ *
+ * (Derivative works does not include programs which link against, run or include
+ * the source verbatim in their source distributions)
+ */
+
+#ifndef EVENTDNS_H
+#define EVENTDNS_H
+
+/* Error codes 0-5 are as described in RFC 1035. */
+#define DNS_ERR_NONE 0
+/* The name server was unable to interpret the query */
+#define DNS_ERR_FORMAT 1
+/* The name server was unable to process this query due to a problem with the
+ * name server */
+#define DNS_ERR_SERVERFAILED 2
+/* The domain name does not exist */
+#define DNS_ERR_NOTEXIST 3
+/* The name server does not support the requested kind of query */
+#define DNS_ERR_NOTIMPL 4
+/* The name server refuses to reform the specified operation for policy
+ * reasons */
+#define DNS_ERR_REFUSED 5
+/* The reply was truncated or ill-formated */
+#define DNS_ERR_TRUNCATED 65
+/* An unknown error occurred */
+#define DNS_ERR_UNKNOWN 66
+/* Communication with the server timed out */
+#define DNS_ERR_TIMEOUT 67
+
+#define DNS_IPv4_A 1
+#define DNS_PTR 2 /* XXXX ???? */
+
+#define DNS_QUERY_NO_SEARCH 1
+
+#define DNS_OPTION_SEARCH 1
+#define DNS_OPTION_NAMESERVERS 2
+#define DNS_OPTION_MISC 4
+#define DNS_OPTIONS_ALL 7
+
+typedef void (*eventdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);
+
+int eventdns_nameserver_add(unsigned long int address);
+int eventdns_count_nameservers(void);
+int eventdns_clear_nameservers_and_suspend(void);
+int eventdns_resume(void);
+int eventdns_nameserver_ip_add(const char *ip_as_string);
+int eventdns_resolve_ipv4(const char *name, int flags, eventdns_callback_type callback, void *ptr);
+struct in_addr;
+int eventdns_resolve_reverse(struct in_addr *addr, int flags, eventdns_callback_type callback, void *ptr);
+int eventdns_resolv_conf_parse(int flags, const char *);
+#ifdef MS_WINDOWS
+int eventdns_config_windows_nameservers(void);
+#endif
+void eventdns_search_clear(void);
+void eventdns_search_add(const char *domain);
+void eventdns_search_ndots_set(const int ndots);
+
+typedef void (*eventdns_debug_log_fn_type)(int warn, const char *msg);
+void eventdns_set_log_fn(eventdns_debug_log_fn_type fn);
+
+#define DNS_NO_SEARCH 1
+
+#endif // !EVENTDNS_H
diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h
new file mode 100644
index 0000000000..a10b3726bb
--- /dev/null
+++ b/src/or/eventdns_tor.h
@@ -0,0 +1,13 @@
+
+#include "orconfig.h"
+#define DNS_USE_OPENSSL_FOR_ID
+#ifndef HAVE_UINT
+typedef unsigned int uint;
+#endif
+#ifndef HAVE_U_CHAR
+typedef unsigned char u_char;
+#endif
+#ifdef MS_WINDOWS
+#define inline __inline
+#endif
+
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index da74e98dd2..93c4cbe553 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -536,7 +536,7 @@ accounting_set_wakeup_time(void)
#define BW_ACCOUNTING_VERSION 1
/** Save all our bandwidth tracking information to disk. Return 0 on
- * success, -1 on failure*/
+ * success, -1 on failure. */
int
accounting_record_bandwidth_usage(time_t now)
{
@@ -545,11 +545,18 @@ accounting_record_bandwidth_usage(time_t now)
char time1[ISO_TIME_LEN+1];
char time2[ISO_TIME_LEN+1];
char *cp = buf;
+ time_t tmp;
/* Format is:
Version\nTime\nTime\nRead\nWrite\nSeconds\nExpected-Rate\n */
format_iso_time(time1, interval_start_time);
format_iso_time(time2, now);
+ /* now check to see if they're valid times -- if they're not,
+ * and we write them, then tor will refuse to start next time. */
+ if (parse_iso_time(time1, &tmp) || parse_iso_time(time2, &tmp)) {
+ log_warn(LD_ACCT, "Created a time that we refused to parse.");
+ return -1;
+ }
tor_snprintf(cp, sizeof(buf),
"%d\n%s\n%s\n"U64_FORMAT"\n"U64_FORMAT"\n%lu\n%lu\n",
BW_ACCOUNTING_VERSION,
@@ -676,7 +683,8 @@ hibernate_hard_limit_reached(void)
static int
hibernate_soft_limit_reached(void)
{
- uint64_t soft_limit = (uint64_t) ((get_options()->AccountingMax) * .95);
+ uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(get_options()->AccountingMax)
+ * .95);
if (!soft_limit)
return 0;
return n_bytes_read_in_interval >= soft_limit
@@ -704,6 +712,7 @@ hibernate_begin(int new_state, time_t now)
/* close listeners. leave control listener(s). */
while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
+ (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) {
log_info(LD_NET,"Closing listener type %d", conn->type);
connection_mark_for_close(conn);
@@ -778,11 +787,12 @@ hibernate_go_dormant(time_t now)
(conn = connection_get_by_type(CONN_TYPE_AP)) ||
(conn = connection_get_by_type(CONN_TYPE_EXIT))) {
if (CONN_IS_EDGE(conn))
- connection_edge_end(conn, END_STREAM_REASON_HIBERNATING,
- conn->cpath_layer);
+ connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING,
+ TO_EDGE_CONN(conn)->cpath_layer);
log_info(LD_NET,"Closing conn type %d", conn->type);
if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */
- connection_mark_unattached_ap(conn, END_STREAM_REASON_HIBERNATING);
+ connection_mark_unattached_ap(TO_EDGE_CONN(conn),
+ END_STREAM_REASON_HIBERNATING);
else
connection_mark_for_close(conn);
}
diff --git a/src/or/main.c b/src/or/main.c
index 336de8b5a7..b2fd0359d7 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -50,13 +50,12 @@ static time_t time_to_fetch_directory = 0;
/** When do we next download a running-routers summary? */
static time_t time_to_fetch_running_routers = 0;
-/** Array of all open connections; each element corresponds to the element of
- * poll_array in the same position. The first nfds elements are valid. */
+/** Array of all open connections. The first n_conns elements are valid. */
static connection_t *connection_array[MAXCONNECTIONS+1] =
{ NULL };
static smartlist_t *closeable_connection_lst = NULL;
-static int nfds=0; /**< Number of connections currently active. */
+static int n_conns=0; /**< Number of connections currently active. */
/** We set this to 1 when we've opened a circuit, so we can print a log
* entry to inform the user that Tor is working. */
@@ -112,8 +111,8 @@ static char* nt_strerror(uint32_t errnum);
/** How old do we let a connection to an OR get before deciding it's
* obsolete? */
#define TIME_BEFORE_OR_CONN_IS_OBSOLETE (60*60*24*7)
-/** How long do we OR connections to handshake before we decide that they
- * could be obsolete? */
+/** How long do we let OR connections handshake before we decide that
+ * they are obsolete? */
#define TLS_HANDSHAKE_TIMEOUT (60)
/********* END VARIABLES ************/
@@ -121,8 +120,7 @@ static char* nt_strerror(uint32_t errnum);
/****************************************************************************
*
* This section contains accessors and other methods on the connection_array
-* and poll_array variables (which are global within this file and unavailable
-* outside it).
+* variables (which are global within this file and unavailable outside it).
*
****************************************************************************/
@@ -136,15 +134,15 @@ connection_add(connection_t *conn)
tor_assert(conn);
tor_assert(conn->s >= 0);
- if (nfds >= get_options()->_ConnLimit-1) {
+ if (n_conns >= get_options()->_ConnLimit-1) {
log_warn(LD_NET,"Failing because we have %d connections already. Please "
- "raise your ulimit -n.", nfds);
+ "raise your ulimit -n.", n_conns);
return -1;
}
- tor_assert(conn->poll_index == -1); /* can only connection_add once */
- conn->poll_index = nfds;
- connection_array[nfds] = conn;
+ tor_assert(conn->conn_array_index == -1); /* can only connection_add once */
+ conn->conn_array_index = n_conns;
+ connection_array[n_conns] = conn;
conn->read_event = tor_malloc_zero(sizeof(struct event));
conn->write_event = tor_malloc_zero(sizeof(struct event));
@@ -153,10 +151,10 @@ connection_add(connection_t *conn)
event_set(conn->write_event, conn->s, EV_WRITE|EV_PERSIST,
conn_write_callback, conn);
- nfds++;
+ n_conns++;
- log_debug(LD_NET,"new conn type %s, socket %d, nfds %d.",
- conn_type_to_string(conn->type), conn->s, nfds);
+ log_debug(LD_NET,"new conn type %s, socket %d, n_conns %d.",
+ conn_type_to_string(conn->type), conn->s, n_conns);
return 0;
}
@@ -171,24 +169,24 @@ connection_remove(connection_t *conn)
int current_index;
tor_assert(conn);
- tor_assert(nfds>0);
+ tor_assert(n_conns>0);
- log_debug(LD_NET,"removing socket %d (type %s), nfds now %d",
- conn->s, conn_type_to_string(conn->type), nfds-1);
+ log_debug(LD_NET,"removing socket %d (type %s), n_conns now %d",
+ conn->s, conn_type_to_string(conn->type), n_conns-1);
- tor_assert(conn->poll_index >= 0);
- current_index = conn->poll_index;
- if (current_index == nfds-1) { /* this is the end */
- nfds--;
+ tor_assert(conn->conn_array_index >= 0);
+ current_index = conn->conn_array_index;
+ if (current_index == n_conns-1) { /* this is the end */
+ n_conns--;
return 0;
}
connection_unregister(conn);
/* replace this one with the one at the end */
- nfds--;
- connection_array[current_index] = connection_array[nfds];
- connection_array[current_index]->poll_index = current_index;
+ n_conns--;
+ connection_array[current_index] = connection_array[n_conns];
+ connection_array[current_index]->conn_array_index = current_index;
return 0;
}
@@ -212,11 +210,11 @@ connection_unlink(connection_t *conn, int remove)
}
smartlist_remove(closeable_connection_lst, conn);
if (conn->type == CONN_TYPE_EXIT) {
- assert_connection_edge_not_dns_pending(conn);
+ assert_connection_edge_not_dns_pending(TO_EDGE_CONN(conn));
}
- if (conn->type == CONN_TYPE_OR &&
- !tor_digest_is_zero(conn->identity_digest)) {
- connection_or_remove_from_identity_map(conn);
+ if (conn->type == CONN_TYPE_OR) {
+ if (!tor_digest_is_zero(TO_OR_CONN(conn)->identity_digest))
+ connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
connection_free(conn);
}
@@ -243,7 +241,7 @@ int
connection_in_array(connection_t *conn)
{
int i;
- for (i=0; i<nfds; ++i) {
+ for (i=0; i<n_conns; ++i) {
if (conn==connection_array[i])
return 1;
}
@@ -258,7 +256,7 @@ void
get_connection_array(connection_t ***array, int *n)
{
*array = connection_array;
- *n = nfds;
+ *n = n_conns;
}
/** Set the event mask on <b>conn</b> to <b>events</b>. (The event
@@ -382,10 +380,10 @@ close_closeable_connections(void)
int i;
for (i = 0; i < smartlist_len(closeable_connection_lst); ) {
connection_t *conn = smartlist_get(closeable_connection_lst, i);
- if (conn->poll_index < 0) {
+ if (conn->conn_array_index < 0) {
connection_unlink(conn, 0); /* blow it away right now */
} else {
- if (!conn_close_if_marked(conn->poll_index))
+ if (!conn_close_if_marked(conn->conn_array_index))
++i;
}
}
@@ -397,6 +395,8 @@ static void
conn_read_callback(int fd, short event, void *_conn)
{
connection_t *conn = _conn;
+ (void)fd;
+ (void)event;
log_debug(LD_NET,"socket %d wants to read.",conn->s);
@@ -411,7 +411,8 @@ conn_read_callback(int fd, short event, void *_conn)
tor_fragile_assert();
#endif
if (CONN_IS_EDGE(conn))
- connection_edge_end_errno(conn, conn->cpath_layer);
+ connection_edge_end_errno(TO_EDGE_CONN(conn),
+ TO_EDGE_CONN(conn)->cpath_layer);
connection_mark_for_close(conn);
}
}
@@ -427,6 +428,8 @@ static void
conn_write_callback(int fd, short events, void *_conn)
{
connection_t *conn = _conn;
+ (void)fd;
+ (void)events;
LOG_FN_CONN(conn, (LOG_DEBUG, LD_NET, "socket %d wants to write.",conn->s));
@@ -439,7 +442,10 @@ conn_write_callback(int fd, short events, void *_conn)
"Bug: unhandled error on write for %s connection (fd %d); removing",
conn_type_to_string(conn->type), conn->s);
tor_fragile_assert();
- conn->has_sent_end = 1; /* otherwise we cry wolf about duplicate close */
+ if (CONN_IS_EDGE(conn)) {
+ /* otherwise we cry wolf about duplicate close */
+ conn->edge_has_sent_end = 1;
+ }
/* XXX do we need a close-immediate here, so we don't try to flush? */
connection_mark_for_close(conn);
}
@@ -485,7 +491,7 @@ conn_close_if_marked(int i)
conn->marked_for_close_file, conn->marked_for_close);
if (connection_speaks_cells(conn)) {
if (conn->state == OR_CONN_STATE_OPEN) {
- retval = flush_buf_tls(conn->tls, conn->outbuf, sz,
+ retval = flush_buf_tls(TO_OR_CONN(conn)->tls, conn->outbuf, sz,
&conn->outbuf_flushlen);
} else
retval = -1; /* never flush non-open broken tls connections */
@@ -534,54 +540,23 @@ void
directory_all_unreachable(time_t now)
{
connection_t *conn;
+ (void)now;
stats_n_seconds_working=0; /* reset it */
while ((conn = connection_get_by_type_state(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT))) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
log_notice(LD_NET,
"Is your network connection down? "
"Failing connection to '%s:%d'.",
- safe_str(conn->socks_request->address),
- conn->socks_request->port);
- connection_mark_unattached_ap(conn, END_STREAM_REASON_NET_UNREACHABLE);
+ safe_str(edge_conn->socks_request->address),
+ edge_conn->socks_request->port);
+ connection_mark_unattached_ap(edge_conn,
+ END_STREAM_REASON_NET_UNREACHABLE);
}
}
-/**
- * Return the interval to wait between directory downloads, in seconds.
- */
-static INLINE int
-get_dir_fetch_period(or_options_t *options)
-{
- if (options->DirFetchPeriod)
- /* Value from config file. */
- return options->DirFetchPeriod;
- else if (options->DirPort)
- /* Default for directory server */
- return 60*60;
- else
- /* Default for average user. */
- return 120*60;
-}
-
-/**
- * Return the interval to wait betweeen router status downloads, in seconds.
- */
-static INLINE int
-get_status_fetch_period(or_options_t *options)
-{
- if (options->StatusFetchPeriod)
- /* Value from config file. */
- return options->StatusFetchPeriod;
- else if (options->DirPort)
- /* Default for directory server */
- return 15*60;
- else
- /* Default for average user. */
- return 30*60;
-}
-
/** This function is called whenever we successfully pull down some new
* network statuses or server descriptors. */
void
@@ -597,14 +572,9 @@ directory_info_has_arrived(time_t now, int from_cache)
return;
}
- if (server_mode(options) &&
- !we_are_hibernating()) { /* connect to the appropriate routers */
- if (!authdir_mode(options))
- router_retry_connections(0, 1);
- if (!from_cache &&
- (has_completed_circuit || !any_predicted_circuits(now)))
- consider_testing_reachability();
- }
+ if (server_mode(options) && !we_are_hibernating() && !from_cache &&
+ (has_completed_circuit || !any_predicted_circuits(now)))
+ consider_testing_reachability(1, 1);
}
/** Perform regular maintenance tasks for a single connection. This
@@ -616,9 +586,10 @@ run_connection_housekeeping(int i, time_t now)
cell_t cell;
connection_t *conn = connection_array[i];
or_options_t *options = get_options();
+ or_connection_t *or_conn;
- if (conn->outbuf && !buf_datalen(conn->outbuf))
- conn->timestamp_lastempty = now;
+ if (conn->outbuf && !buf_datalen(conn->outbuf) && conn->type == CONN_TYPE_OR)
+ TO_OR_CONN(conn)->timestamp_lastempty = now;
if (conn->marked_for_close) {
/* nothing to do here */
@@ -640,7 +611,7 @@ run_connection_housekeeping(int i, time_t now)
buf_datalen(conn->inbuf)>=1024) {
log_info(LD_DIR,"Trying to extract information from wedged server desc "
"download.");
- connection_dir_reached_eof(conn);
+ connection_dir_reached_eof(TO_DIR_CONN(conn));
} else {
connection_mark_for_close(conn);
}
@@ -650,17 +621,19 @@ run_connection_housekeeping(int i, time_t now)
if (!connection_speaks_cells(conn))
return; /* we're all done here, the rest is just for OR conns */
- if (!conn->is_obsolete) {
+ or_conn = TO_OR_CONN(conn);
+
+ if (!conn->or_is_obsolete) {
if (conn->timestamp_created + TIME_BEFORE_OR_CONN_IS_OBSOLETE < now) {
log_info(LD_OR,
"Marking OR conn to %s:%d obsolete (fd %d, %d secs old).",
conn->address, conn->port, conn->s,
(int)(now - conn->timestamp_created));
- conn->is_obsolete = 1;
+ conn->or_is_obsolete = 1;
} else {
- connection_t *best =
- connection_or_get_by_identity_digest(conn->identity_digest);
- if (best && best != conn &&
+ or_connection_t *best =
+ connection_or_get_by_identity_digest(or_conn->identity_digest);
+ if (best && best != or_conn &&
(conn->state == OR_CONN_STATE_OPEN ||
now > conn->timestamp_created + TLS_HANDSHAKE_TIMEOUT)) {
/* We only mark as obsolete connections that already are in
@@ -675,16 +648,16 @@ run_connection_housekeeping(int i, time_t now)
"(fd %d, %d secs old).",
conn->address, conn->port, conn->s,
(int)(now - conn->timestamp_created));
- conn->is_obsolete = 1;
+ conn->or_is_obsolete = 1;
}
}
}
- if (conn->is_obsolete && !conn->n_circuits) {
+ if (conn->or_is_obsolete && !or_conn->n_circuits) {
/* no unmarked circs -- mark it now */
log_info(LD_OR,
"Expiring non-used OR connection to fd %d (%s:%d) [Obsolete].",
- conn->s,conn->address, conn->port);
+ conn->s, conn->address, conn->port);
connection_mark_for_close(conn);
conn->hold_open_until_flushed = 1;
return;
@@ -693,20 +666,20 @@ run_connection_housekeeping(int i, time_t now)
/* If we haven't written to an OR connection for a while, then either nuke
the connection or send a keepalive, depending. */
if (now >= conn->timestamp_lastwritten + options->KeepalivePeriod) {
- routerinfo_t *router = router_get_by_digest(conn->identity_digest);
+ routerinfo_t *router = router_get_by_digest(or_conn->identity_digest);
if (!connection_state_is_open(conn)) {
log_info(LD_OR,"Expiring non-open OR connection to fd %d (%s:%d).",
conn->s,conn->address, conn->port);
connection_mark_for_close(conn);
conn->hold_open_until_flushed = 1;
- } else if (we_are_hibernating() && !conn->n_circuits &&
+ } else if (we_are_hibernating() && !or_conn->n_circuits &&
!buf_datalen(conn->outbuf)) {
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[Hibernating or exiting].",
conn->s,conn->address, conn->port);
connection_mark_for_close(conn);
conn->hold_open_until_flushed = 1;
- } else if (!clique_mode(options) && !conn->n_circuits &&
+ } else if (!clique_mode(options) && !or_conn->n_circuits &&
(!router || !server_mode(options) ||
!router_is_clique_mode(router))) {
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
@@ -715,7 +688,7 @@ run_connection_housekeeping(int i, time_t now)
connection_mark_for_close(conn);
conn->hold_open_until_flushed = 1;
} else if (
- now >= conn->timestamp_lastempty + options->KeepalivePeriod*10 &&
+ now >= or_conn->timestamp_lastempty + options->KeepalivePeriod*10 &&
now >= conn->timestamp_lastwritten + options->KeepalivePeriod*10) {
log_fn(LOG_PROTOCOL_WARN,LD_PROTOCOL,
"Expiring stuck OR connection to fd %d (%s:%d). (%d bytes to "
@@ -730,7 +703,7 @@ run_connection_housekeeping(int i, time_t now)
conn->address, conn->port);
memset(&cell,0,sizeof(cell_t));
cell.command = CELL_PADDING;
- connection_or_write_cell_to_buf(&cell, conn);
+ connection_or_write_cell_to_buf(&cell, or_conn);
}
}
}
@@ -749,6 +722,7 @@ run_scheduled_events(time_t now)
static time_t time_to_try_getting_descriptors = 0;
static time_t time_to_reset_descriptor_failures = 0;
static time_t time_to_add_entropy = 0;
+ static time_t time_to_check_for_wildcarded_dns = 0;
or_options_t *options = get_options();
int i;
int have_dir_info;
@@ -769,13 +743,15 @@ run_scheduled_events(time_t now)
rotate_onion_key();
cpuworkers_rotate();
if (router_rebuild_descriptor(1)<0) {
- log_warn(LD_BUG, "Couldn't rebuild router descriptor");
+ log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
}
if (advertised_server_mode())
router_upload_dir_desc_to_dirservers(0);
}
if (time_to_try_getting_descriptors < now) {
+ /* XXXX Maybe we should do this every 10sec when not enough info,
+ * and every 60sec when we have enough info -NM */
update_router_descriptor_downloads(now);
time_to_try_getting_descriptors = now + DESCRIPTOR_RETRY_INTERVAL;
}
@@ -791,7 +767,7 @@ run_scheduled_events(time_t now)
last_rotated_certificate = now;
if (last_rotated_certificate+MAX_SSL_KEY_LIFETIME < now) {
log_info(LD_GENERAL,"Rotating tls context.");
- if (tor_tls_context_new(get_identity_key(), 1, options->Nickname,
+ if (tor_tls_context_new(get_identity_key(), options->Nickname,
MAX_SSL_KEY_LIFETIME) < 0) {
log_warn(LD_BUG, "Error reinitializing TLS context");
/* XXX is it a bug here, that we just keep going? */
@@ -816,7 +792,7 @@ run_scheduled_events(time_t now)
if (now % 10 == 0 && authdir_mode(options) && !we_are_hibernating()) {
/* try to determine reachability */
- router_retry_connections(1, 0);
+ dirserv_test_reachability(0);
}
/** 2. Periodically, we consider getting a new directory, getting a
@@ -835,7 +811,8 @@ run_scheduled_events(time_t now)
directory_get_from_dirserver(DIR_PURPOSE_FETCH_DIR, NULL, 1);
}
- time_to_fetch_directory = now + get_dir_fetch_period(options);
+#define V1_DIR_FETCH_PERIOD (60*60)
+ time_to_fetch_directory = now + V1_DIR_FETCH_PERIOD;
/* Also, take this chance to remove old information from rephist
* and the rend cache. */
@@ -851,7 +828,8 @@ run_scheduled_events(time_t now)
if (!authdir_mode(options) || !options->V1AuthoritativeDir) {
directory_get_from_dirserver(DIR_PURPOSE_FETCH_RUNNING_LIST, NULL, 1);
}
- time_to_fetch_running_routers = now + get_status_fetch_period(options);
+#define V1_RUNNINGROUTERS_FETCH_PERIOD (20*60)
+ time_to_fetch_running_routers = now + V1_RUNNINGROUTERS_FETCH_PERIOD;
}
/* 2b. Once per minute, regenerate and upload the descriptor if the old
@@ -865,14 +843,14 @@ run_scheduled_events(time_t now)
}
mark_my_descriptor_dirty_if_older_than(
now - FORCE_REGENERATE_DESCRIPTOR_INTERVAL);
- consider_publishable_server(now, 0);
+ consider_publishable_server(0);
/* also, check religiously for reachability, if it's within the first
* 20 minutes of our uptime. */
if (server_mode(options) &&
(has_completed_circuit || !any_predicted_circuits(now)) &&
stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT &&
!we_are_hibernating())
- consider_testing_reachability();
+ consider_testing_reachability(1, 1);
/* If any networkstatus documents are no longer recent, we need to
* update all the descriptors' running status. */
@@ -920,11 +898,11 @@ run_scheduled_events(time_t now)
circuit_build_needed_circs(now);
/** 5. We do housekeeping for each connection... */
- for (i=0;i<nfds;i++) {
+ for (i=0;i<n_conns;i++) {
run_connection_housekeeping(i, now);
}
if (time_to_shrink_buffers < now) {
- for (i=0;i<nfds;i++) {
+ for (i=0;i<n_conns;i++) {
connection_t *conn = connection_array[i];
if (conn->outbuf)
buf_shrink(conn->outbuf);
@@ -946,6 +924,18 @@ run_scheduled_events(time_t now)
* we'll pass it to poll/select and bad things will happen.
*/
close_closeable_connections();
+
+ /** 9. and if we're a server, check whether our DNS is telling stories to
+ * us. */
+ if (server_mode(options) && time_to_check_for_wildcarded_dns < now) {
+ if (!time_to_check_for_wildcarded_dns) {
+ time_to_check_for_wildcarded_dns = now + 60 + crypto_rand_int(120);
+ } else {
+ dns_launch_wildcard_checks();
+ time_to_check_for_wildcarded_dns = now + 12*3600 +
+ crypto_rand_int(12*3600);
+ }
+ }
}
static struct event *timeout_event = NULL;
@@ -962,6 +952,9 @@ second_elapsed_callback(int fd, short event, void *args)
size_t bytes_read;
int seconds_elapsed;
or_options_t *options = get_options();
+ (void)fd;
+ (void)event;
+ (void)args;
if (!timeout_event) {
timeout_event = tor_malloc_zero(sizeof(struct event));
evtimer_set(timeout_event, second_elapsed_callback, NULL);
@@ -1014,9 +1007,11 @@ second_elapsed_callback(int fd, short event, void *args)
me->address, me->dir_port);
}
- /* if more than 100s have elapsed, probably the clock jumped: doesn't
- * count. */
- if (seconds_elapsed < 100)
+/** If more than this many seconds have elapsed, probably the clock
+ * jumped: doesn't count. */
+#define NUM_JUMPED_SECONDS_BEFORE_WARN 10
+/* This used to be 100, but I cranked it down for Mike Chiussi -RD */
+ if (seconds_elapsed < NUM_JUMPED_SECONDS_BEFORE_WARN)
stats_n_seconds_working += seconds_elapsed;
else
circuit_note_clock_jumped(seconds_elapsed);
@@ -1056,7 +1051,6 @@ got_libevent_error(void)
static int
do_hup(void)
{
- char keydir[512];
or_options_t *options = get_options();
log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config.");
@@ -1076,11 +1070,8 @@ do_hup(void)
options = get_options(); /* they have changed now */
if (authdir_mode(options)) {
/* reload the approved-routers file */
- tor_snprintf(keydir, sizeof(keydir),
- "%s/approved-routers", options->DataDirectory);
- log_info(LD_GENERAL,
- "Reloading approved fingerprints from \"%s\"...", keydir);
- if (dirserv_parse_fingerprint_file(keydir) < 0) {
+ if (dirserv_load_fingerprint_file() < 0) {
+ /* warnings are logged from dirserv_load_fingerprint_file() directly */
log_info(LD_GENERAL, "Error reloading fingerprints. "
"Continuing with old list.");
}
@@ -1101,11 +1092,14 @@ do_hup(void)
if (server_mode(options)) {
// const char *descriptor;
+ mark_my_descriptor_dirty();
/* Restart cpuworker and dnsworker processes, so they get up-to-date
* configuration options. */
cpuworkers_rotate();
- dnsworkers_rotate();
+ dns_reset();
#if 0
+ const char *descriptor;
+ char keydir[512];
/* Write out a fresh descriptor, but leave old one on failure. */
router_rebuild_descriptor(1);
descriptor = router_get_my_descriptor();
@@ -1128,7 +1122,11 @@ do_main_loop(void)
{
int loop_result;
- dns_init(); /* initialize dns resolve map, spawn workers if needed */
+ /* initialize dns resolve map, spawn workers if needed */
+ if (dns_init() < 0) {
+ log_err(LD_GENERAL,"Error initializing dns subsystem; exiting");
+ return -1;
+ }
handle_signals(1);
@@ -1160,7 +1158,7 @@ do_main_loop(void)
if (authdir_mode(get_options())) {
/* the directory is already here, run startup things */
- router_retry_connections(1, 1);
+ dirserv_test_reachability(1);
}
if (server_mode(get_options())) {
@@ -1219,16 +1217,33 @@ do_main_loop(void)
}
}
+/* DOCDOC */
+int
+control_signal_check(int the_signal)
+{
+ switch (the_signal)
+ {
+ case 1:
+ case 2:
+ case 10:
+ case 12:
+ case 15:
+ case SIGNEWNYM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
/** Used to implement the SIGNAL control command: if we accept
- * <b>the_signal</b> as a remote pseudo-signal, then act on it and
- * return 0. Else return -1. */
+ * <b>the_signal</b> as a remote pseudo-signal, act on it. */
/* We don't re-use catch() here because:
* 1. We handle a different set of signals than those allowed in catch.
* 2. Platforms without signal() are unlikely to define SIGfoo.
* 3. The control spec is defined to use fixed numeric signal values
* which just happen to match the unix values.
*/
-int
+void
control_signal_act(int the_signal)
{
switch (the_signal)
@@ -1252,9 +1267,9 @@ control_signal_act(int the_signal)
signal_callback(0,0,(void*)(uintptr_t)SIGNEWNYM);
break;
default:
- return -1;
+ log_warn(LD_BUG, "Unrecognized signal number %d.", the_signal);
+ break;
}
- return 0;
}
/** Libevent callback: invoked when we get a signal.
@@ -1263,6 +1278,8 @@ static void
signal_callback(int fd, short events, void *arg)
{
uintptr_t sig = (uintptr_t)arg;
+ (void)fd;
+ (void)events;
switch (sig)
{
case SIGTERM:
@@ -1311,21 +1328,21 @@ signal_callback(int fd, short events, void *arg)
}
}
+extern uint64_t buf_total_used;
+extern uint64_t buf_total_alloc;
+extern uint64_t rephist_total_alloc;
+extern uint32_t rephist_total_num;
+
/**
* Write current memory usage information to the log.
*/
static void
dumpmemusage(int severity)
{
- extern uint64_t buf_total_used;
- extern uint64_t buf_total_alloc;
- extern uint64_t rephist_total_alloc;
- extern uint32_t rephist_total_num;
-
log(severity, LD_GENERAL,
"In buffers: "U64_FORMAT" used/"U64_FORMAT" allocated (%d conns).",
U64_PRINTF_ARG(buf_total_used), U64_PRINTF_ARG(buf_total_alloc),
- nfds);
+ n_conns);
log(severity, LD_GENERAL, "In rephist: "U64_FORMAT" used by %d Tors.",
U64_PRINTF_ARG(rephist_total_alloc), rephist_total_num);
dump_routerlist_mem_usage(severity);
@@ -1343,7 +1360,7 @@ dumpstats(int severity)
log(severity, LD_GENERAL, "Dumping stats:");
- for (i=0;i<nfds;i++) {
+ for (i=0;i<n_conns;i++) {
conn = connection_array[i];
log(severity, LD_GENERAL,
"Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago",
@@ -1352,7 +1369,7 @@ dumpstats(int severity)
(int)(now - conn->timestamp_created));
if (!connection_is_listener(conn)) {
log(severity,LD_GENERAL,
- "Conn %d is to '%s:%d'.", i,
+ "Conn %d is to %s:%d.", i,
safe_str(conn->address), conn->port);
log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)",
@@ -1387,12 +1404,12 @@ dumpstats(int severity)
U64_PRINTF_ARG(stats_n_destroy_cells_processed));
if (stats_n_data_cells_packaged)
log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%",
- 100*(((double)stats_n_data_bytes_packaged) /
- (stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
+ 100*(U64_TO_DBL(stats_n_data_bytes_packaged) /
+ U64_TO_DBL(stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
if (stats_n_data_cells_received)
log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%",
- 100*(((double)stats_n_data_bytes_received) /
- (stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
+ 100*(U64_TO_DBL(stats_n_data_bytes_received) /
+ U64_TO_DBL(stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
if (now - time_of_process_start >= 0)
elapsed = now - time_of_process_start;
@@ -1504,8 +1521,7 @@ tor_init(int argc, char *argv[])
atexit(exit_function);
if (options_init_from_torrc(argc,argv) < 0) {
- log_err(LD_CONFIG,"Reading config failed--see warnings above. "
- "For usage, try -h.");
+ log_err(LD_CONFIG,"Reading config failed--see warnings above.");
return -1;
}
@@ -1582,30 +1598,32 @@ tor_cleanup(void)
}
/** Read/create keys as needed, and echo our fingerprint to stdout. */
-static void
+static int
do_list_fingerprint(void)
{
char buf[FINGERPRINT_LEN+1];
crypto_pk_env_t *k;
const char *nickname = get_options()->Nickname;
if (!server_mode(get_options())) {
- printf("Clients don't have long-term identity keys. Exiting.\n");
- return;
+ log_err(LD_GENERAL,
+ "Clients don't have long-term identity keys. Exiting.\n");
+ return -1;
}
tor_assert(nickname);
if (init_keys() < 0) {
log_err(LD_BUG,"Error initializing keys; exiting");
- return;
+ return -1;
}
if (!(k = get_identity_key())) {
log_err(LD_GENERAL,"Error: missing identity key.");
- return;
+ return -1;
}
if (crypto_pk_get_fingerprint(k, buf, 1)<0) {
- log_warn(LD_BUG, "Error computing fingerprint");
- return;
+ log_err(LD_BUG, "Error computing fingerprint");
+ return -1;
}
printf("%s %s\n", nickname, buf);
+ return 0;
}
/** Entry point for password hashing: take the desired password from
@@ -1773,7 +1791,7 @@ nt_service_main(void)
if (!StartServiceCtrlDispatcher(table)) {
result = GetLastError();
errmsg = nt_strerror(result);
- printf("Service error %d : %s\n", result, errmsg);
+ printf("Service error %d : %s\n", (int) result, errmsg);
LocalFree(errmsg);
if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
if (tor_init(backup_argc, backup_argv) < 0)
@@ -1897,7 +1915,7 @@ nt_service_stop(SC_HANDLE hService)
}
else {
errmsg = nt_strerror(GetLastError());
- printf("Service failed to stop : %s\n");
+ printf("Service failed to stop : %s\n",errmsg);
LocalFree(errmsg);
}
}
@@ -2010,7 +2028,6 @@ nt_service_remove(void)
{
SC_HANDLE hSCManager = NULL;
SC_HANDLE hService = NULL;
- BOOL result = FALSE;
char *errmsg;
if ((hSCManager = nt_service_open_scm()) == NULL) {
@@ -2114,10 +2131,11 @@ _tor_dmalloc_free(void *p)
int
tor_main(int argc, char *argv[])
{
+ int result = 0;
#ifdef USE_DMALLOC
- int r = CRYPTO_set_mem_ex_functions(_tor_malloc, _tor_realloc,
- _tor_dmalloc_free);
- log_notice(LD_CONFIG, "Set up damalloc; returned %d", r);
+ int r = CRYPTO_set_mem_ex_functions(_tor_malloc, _tor_realloc,
+ _tor_dmalloc_free);
+ log_notice(LD_CONFIG, "Set up dmalloc; returned %d", r);
#endif
#ifdef MS_WINDOWS_SERVICE
backup_argv = argv;
@@ -2154,22 +2172,26 @@ tor_main(int argc, char *argv[])
#ifdef MS_WINDOWS_SERVICE
service_status.dwCurrentState = SERVICE_RUNNING;
#endif
- do_main_loop();
+ result = do_main_loop();
break;
case CMD_LIST_FINGERPRINT:
- do_list_fingerprint();
+ result = do_list_fingerprint();
break;
case CMD_HASH_PASSWORD:
do_hash_password();
+ result = 0;
break;
case CMD_VERIFY_CONFIG:
printf("Configuration was valid\n");
+ result = 0;
break;
+ case CMD_RUN_UNITTESTS: /* only set by test.c */
default:
log_warn(LD_BUG,"Illegal command number %d: internal error.",
get_options()->command);
+ result = -1;
}
tor_cleanup();
- return -1;
+ return result;
}
diff --git a/src/or/onion.c b/src/or/onion.c
index 315c5f12dd..ba41f21a22 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -17,7 +17,7 @@ const char onion_c_id[] =
/** Type for a linked list of circuits that are waiting for a free CPU worker
* to process a waiting onion handshake. */
typedef struct onion_queue_t {
- circuit_t *circ;
+ or_circuit_t *circ;
time_t when_added;
struct onion_queue_t *next;
} onion_queue_t;
@@ -35,7 +35,7 @@ static int ol_length=0;
* if ol_list is too long, in which case do nothing and return -1.
*/
int
-onion_pending_add(circuit_t *circ)
+onion_pending_add(or_circuit_t *circ)
{
onion_queue_t *tmp;
time_t now = time(NULL);
@@ -75,7 +75,7 @@ onion_pending_add(circuit_t *circ)
onion_pending_remove(ol_list->circ);
log_info(LD_CIRC,
"Circuit create request is too old; cancelling due to overload.");
- circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
}
return 0;
}
@@ -83,10 +83,10 @@ onion_pending_add(circuit_t *circ)
/** Remove the first item from ol_list and return it, or return
* NULL if the list is empty.
*/
-circuit_t *
+or_circuit_t *
onion_next_task(void)
{
- circuit_t *circ;
+ or_circuit_t *circ;
if (!ol_list)
return NULL; /* no onions pending, we're done */
@@ -103,7 +103,7 @@ onion_next_task(void)
* circ, remove and free that element. Leave circ itself alone.
*/
void
-onion_pending_remove(circuit_t *circ)
+onion_pending_remove(or_circuit_t *circ)
{
onion_queue_t *tmpo, *victim;
diff --git a/src/or/or.h b/src/or/or.h
index b529fcb617..a74a892dd2 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -22,7 +22,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <limits.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -38,30 +37,10 @@
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
-#include "../common/torint.h"
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h> /* FreeBSD needs this to know what version it is */
#endif
-#ifdef HAVE_SYS_LIMITS_H
-#include <sys/limits.h>
-#endif
-#ifdef HAVE_MACHINE_LIMITS_H
-#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
- /* FreeBSD has a bug where it complains that this file is obsolete,
- and I should migrate to using sys/limits. It complains even when
- I include both.
- __FreeBSD_kernel__ is defined by Debian GNU/kFreeBSD which
- does the same thing (but doesn't defined __FreeBSD__).
- */
-#include <machine/limits.h>
-#endif
-#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h> /* Must be included before sys/stat.h for Ultrix */
-#endif
+#include "../common/torint.h"
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
@@ -101,7 +80,13 @@
/** Upper bound on maximum simultaneous connections; can be lowered by
* config file. */
+#ifdef CYGWIN
+/* http://archives.seul.org/or/talk/Aug-2006/msg00210.html */
+#define MAXCONNECTIONS 3200
+#else
+/* very high by default. "nobody should need more than this..." */
#define MAXCONNECTIONS 15000
+#endif
#ifdef MS_WINDOWS
/* No, we don't need to redefine FD_SETSIZE before including winsock:
@@ -170,7 +155,6 @@
#define cell_t tor_cell_t
#endif
-#define DEFAULT_BANDWIDTH_OP (1024 * 1000)
#define MAX_NICKNAME_LEN 19
/* Hex digest plus dollar sign. */
#define MAX_HEX_NICKNAME_LEN (HEX_DIGEST_LEN+1)
@@ -182,12 +166,14 @@
#define MAX_HEADERS_SIZE 50000
#define MAX_BODY_SIZE 500000
-#ifdef TOR_PERF
-/** How long do we keep DNS cache entries before purging them? */
-#define MAX_DNS_ENTRY_AGE (150*60)
-#else
+/** How long do we keep DNS cache entries before purging them (regardless of
+ * their TTL)? */
#define MAX_DNS_ENTRY_AGE (30*60)
-#endif
+#define DEFAULT_DNS_TTL (30*60)
+/** How long can a TTL be before we stop believing it? */
+#define MAX_DNS_TTL (3*60*60)
+/** How small can a TTL be before we stop believing it? */
+#define MIN_DNS_TTL (60)
/** How often do we rotate onion keys? */
#define MIN_ONION_KEY_LIFETIME (7*24*60*60)
@@ -235,7 +221,9 @@ typedef enum {
#define CONN_TYPE_CONTROL_LISTENER 12
/** Type for connections from user interface process. */
#define CONN_TYPE_CONTROL 13
-#define _CONN_TYPE_MAX 13
+/** Type for sockets listening for transparent proxy connections. */
+#define CONN_TYPE_AP_TRANS_LISTENER 14
+#define _CONN_TYPE_MAX 14
#define CONN_IS_EDGE(x) \
((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
@@ -303,7 +291,10 @@ typedef enum {
#define AP_CONN_STATE_RESOLVE_WAIT 10
/** State for a SOCKS connection: ready to send and receive. */
#define AP_CONN_STATE_OPEN 11
-#define _AP_CONN_STATE_MAX 11
+/** State for a transparent proxy connection: waiting for original
+ * destination. */
+#define AP_CONN_STATE_ORIGDST_WAIT 12
+#define _AP_CONN_STATE_MAX 12
#define _DIR_CONN_STATE_MIN 1
/** State for connection to directory server: waiting for connect(). */
@@ -496,6 +487,7 @@ typedef enum {
#define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0
#define RESOLVED_TYPE_ERROR 0xF1
+/* XXX We should document the meaning of these. */
#define END_CIRC_AT_ORIGIN -1
#define _END_CIRC_REASON_MIN 0
#define END_CIRC_REASON_NONE 0
@@ -597,7 +589,11 @@ typedef struct {
typedef struct buf_t buf_t;
typedef struct socks_request_t socks_request_t;
-#define CONNECTION_MAGIC 0x7C3C304Eu
+#define BASE_CONNECTION_MAGIC 0x7C3C304Eu
+#define OR_CONNECTION_MAGIC 0x7D31FF03u
+#define EDGE_CONNECTION_MAGIC 0xF0374013u
+#define DIR_CONNECTION_MAGIC 0x9988ffeeu
+#define CONTROL_CONNECTION_MAGIC 0x8abc765du
/** Description of a connection to another host or process, and associated
* data.
@@ -617,48 +613,51 @@ typedef struct socks_request_t socks_request_t;
* conn->outbuf. Connections differ primarily in the functions called
* to fill and drain these buffers.
*/
-struct connection_t {
- uint32_t magic; /**< For memory debugging: must equal CONNECTION_MAGIC. */
+typedef struct connection_t {
+ uint32_t magic; /**< For memory debugging: must equal one of
+ * *_CONNECTION_MAGIC. */
uint8_t type; /**< What kind of connection is this? */
uint8_t state; /**< Current state of this connection. */
- uint8_t purpose; /**< Only used for DIR types currently. */
+ uint8_t purpose; /**< Only used for DIR and EXIT types currently. */
+
+ /* The next fields are all one-bit booleans. Some are only applicable
+ * to connection subtypes, but we hold them here anyway, to save space.
+ * (Currently, they all fit into a single byte.) */
unsigned wants_to_read:1; /**< Boolean: should we start reading again once
- * the bandwidth throttler allows it?
- */
+ * the bandwidth throttler allows it? */
unsigned wants_to_write:1; /**< Boolean: should we start writing again once
- * the bandwidth throttler allows reads?
- */
+ * the bandwidth throttler allows reads? */
unsigned hold_open_until_flushed:1; /**< Despite this connection's being
* marked for close, do we flush it
- * before closing it?
- */
- unsigned has_sent_end:1; /**< For debugging; only used on edge connections.
- * Set once we've set the stream end,
+ * before closing it? */
+ unsigned int inbuf_reached_eof:1; /**< Boolean: did read() return 0 on this
+ * conn? */
+ unsigned edge_has_sent_end:1; /**< For debugging; only used on edge
+ * connections. Set once we've set the stream end,
* and check in circuit_about_to_close_connection(). */
/** For control connections only. If set, we send extended info with control
* events as appropriate. */
unsigned int control_events_are_extended:1;
/** Used for OR conns that shouldn't get any new circs attached to them. */
- unsigned int is_obsolete:1;
+ unsigned int or_is_obsolete:1;
+ /** For AP connections only. If 1, and we fail to reach the chosen exit,
+ * stop requiring it. */
+ unsigned int chosen_exit_optional:1;
int s; /**< Our socket; -1 if this connection is closed. */
- int poll_index; /* XXXX rename. */
- struct event *read_event; /**< libevent event structure. */
- struct event *write_event; /**< libevent event structure. */
+ int conn_array_index; /**< Index into the global connection array. */
+ struct event *read_event; /**< Libevent event structure. */
+ struct event *write_event; /**< Libevent event structure. */
buf_t *inbuf; /**< Buffer holding data read over this connection. */
- int inbuf_reached_eof; /**< Boolean: did read() return 0 on this conn? */
- time_t timestamp_lastread; /**< When was the last time poll() said we could
- * read? */
-
buf_t *outbuf; /**< Buffer holding data to write over this connection. */
size_t outbuf_flushlen; /**< How much data should we try to flush from the
* outbuf? */
- time_t timestamp_lastwritten; /**< When was the last time poll() said we
+ time_t timestamp_lastread; /**< When was the last time libevent said we could
+ * read? */
+ time_t timestamp_lastwritten; /**< When was the last time libevent said we
* could write? */
-
time_t timestamp_created; /**< When was this connection_t created? */
- time_t timestamp_lastempty; /**< When was the outbuf last completely empty?*/
uint32_t addr; /**< IP of the other side of the connection; used to identify
* routers, along with port. */
@@ -667,81 +666,166 @@ struct connection_t {
uint16_t marked_for_close; /**< Should we close this conn on the next
* iteration of the main loop? (If true, holds
* the line number where this connection was
- * marked.)
- */
+ * marked.) */
const char *marked_for_close_file; /**< For debugging: in which file were
* we marked for close? */
char *address; /**< FQDN (or IP) of the guy on the other end.
- * strdup into this, because free_connection frees it.
- */
- crypto_pk_env_t *identity_pkey; /**< Public RSA key for the other side's
- * signing key. */
- char identity_digest[DIGEST_LEN]; /**< Hash of identity_pkey */
+ * strdup into this, because free_connection frees it. */
+
+} connection_t;
+
+/** Subtype of connection_t for an "OR connection" -- that is, one that speaks
+ * cells over TLS. */
+typedef struct or_connection_t {
+ connection_t _base;
+
+ char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for
+ * the other side's signing key. */
char *nickname; /**< Nickname of OR on other side (if any). */
- /** Nickname of planned exit node -- used with .exit support. */
- char *chosen_exit_name;
+ tor_tls_t *tls; /**< TLS connection state */
-/* Used only by OR connections: */
- tor_tls_t *tls; /**< TLS connection state (OR only.) */
+ time_t timestamp_lastempty; /**< When was the outbuf last completely empty?*/
- /* bandwidth and receiver_bucket only used by ORs in OPEN state: */
- int bandwidth; /**< Connection bandwidth. (OPEN ORs only.) */
+ /* bandwidth* and receiver_bucket only used by ORs in OPEN state: */
+ int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */
+ int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */
int receiver_bucket; /**< When this hits 0, stop receiving. Every second we
- * add 'bandwidth' to this, capping it at 10*bandwidth.
- * (OPEN ORs only)
- */
+ * add 'bandwidthrate' to this, capping it at
+ * bandwidthburst. (OPEN ORs only) */
circ_id_type_t circ_id_type; /**< When we send CREATE cells along this
* connection, which half of the space should
* we use? */
int n_circuits; /**< How many circuits use this connection as p_conn or
* n_conn ? */
- struct connection_t *next_with_same_id; /**< Next connection with same
+ struct or_connection_t *next_with_same_id; /**< Next connection with same
* identity digest as this one. */
uint16_t next_circ_id; /**< Which circ_id do we try to use next on
* this connection? This is always in the
- * range 0..1<<15-1. (OR only.)*/
+ * range 0..1<<15-1. */
+} or_connection_t;
+
+/** Subtype of connection_t for an "edge connection" -- that is, a socks (ap)
+ * connection, or an exit. */
+typedef struct edge_connection_t {
+ connection_t _base;
-/* Used only by edge connections: */
- uint16_t stream_id;
- struct connection_t *next_stream; /**< Points to the next stream at this
- * edge, if any (Edge only). */
+ struct edge_connection_t *next_stream; /**< Points to the next stream at this
+ * edge, if any */
struct crypt_path_t *cpath_layer; /**< A pointer to which node in the circ
- * this conn exits at. (Edge only.) */
- int package_window; /**< How many more relay cells can i send into the
- * circuit? (Edge only.) */
- int deliver_window; /**< How many more relay cells can end at me? (Edge
- * only.) */
+ * this conn exits at. */
+ int package_window; /**< How many more relay cells can I send into the
+ * circuit? */
+ int deliver_window; /**< How many more relay cells can end at me? */
-/* Used only by Dir connections */
- char *requested_resource; /**< Which 'resource' did we ask the directory
- * for?*/
+ /** Nickname of planned exit node -- used with .exit support. */
+ char *chosen_exit_name;
-/* Used only by AP connections */
socks_request_t *socks_request; /**< SOCKS structure describing request (AP
* only.) */
+ struct circuit_t *on_circuit; /**< The circuit (if any) that this edge
+ * connection is using. */
+
+ uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
+ * connection. Exit connections only. */
+
+ uint16_t stream_id; /**< The stream ID used for this edge connection on its
+ * circuit */
/** Quasi-global identifier for this connection; used for control.c */
- /* XXXX NM This can get re-used after 2**32 circuits. */
+ /* XXXX NM This can get re-used after 2**32 streams */
uint32_t global_identifier;
- /* Used only by control connections */
- uint32_t event_mask;
- uint32_t incoming_cmd_len;
- uint32_t incoming_cmd_cur_len;
- char *incoming_cmd;
+ char rend_query[REND_SERVICE_ID_LEN+1]; /**< What rendezvous service are we
+ * querying for? (AP only) */
+
+ /** Number of times we've reassigned this application connection to
+ * a new circuit. We keep track because the timeout is longer if we've
+ * already retried several times. */
+ uint8_t num_socks_retries;
+
+} edge_connection_t;
+
+/** Subtype of connection_t for an "directory connection" -- that is, an HTTP
+ * connection to retrieve or serve directory material. */
+typedef struct dir_connection_t {
+ connection_t _base;
+
+ char *requested_resource; /**< Which 'resource' did we ask the directory
+ * for? */
+ unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */
+
+ /* Used only for server sides of some dir connections, to implement
+ * "spooling" of directory material to the outbuf. Otherwise, we'd have
+ * to append everything to the outbuf in one enormous chunk. */
+ enum {
+ DIR_SPOOL_NONE=0, DIR_SPOOL_SERVER_BY_DIGEST, DIR_SPOOL_SERVER_BY_FP,
+ DIR_SPOOL_CACHED_DIR, DIR_SPOOL_NETWORKSTATUS
+ } dir_spool_src;
+ smartlist_t *fingerprint_stack;
+ struct cached_dir_t *cached_dir;
+ off_t cached_dir_offset;
+ tor_zlib_state_t *zlib_state;
-/* Used only by DIR and AP connections: */
- struct circuit_t *on_circuit; /**< The circuit (if any) that this edge
- * connection is using. */
char rend_query[REND_SERVICE_ID_LEN+1]; /**< What rendezvous service are we
- * querying for? (DIR/AP only) */
+ * querying for? */
+
+ char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for
+ * the directory server's signing key. */
+} dir_connection_t;
+/** Subtype of connection_t for an connection to a controller. */
+typedef struct control_connection_t {
+ connection_t _base;
+
+ uint32_t event_mask; /**< Bitfield: which events does this controller
+ * care about? */
+ uint32_t incoming_cmd_len;
+ uint32_t incoming_cmd_cur_len;
+ char *incoming_cmd;
/* Used only by control v0 connections */
uint16_t incoming_cmd_type;
-};
-
-typedef struct connection_t connection_t;
+} control_connection_t;
+
+/** Cast a connection_t subtype pointer to a connection_t **/
+#define TO_CONN(c) &(((c)->_base))
+/** Helper macro: Given a pointer to to._base, of type from*, return &to. */
+#define DOWNCAST(to, ptr) \
+ (to*) (((char*)(ptr)) - STRUCT_OFFSET(to, _base))
+
+/** Convert a connection_t* to an or_connection_t*; assert if the cast is
+ * invalid. */
+static or_connection_t *TO_OR_CONN(connection_t *);
+/** Convert a connection_t* to a dir_connection_t*; assert if the cast is
+ * invalid. */
+static dir_connection_t *TO_DIR_CONN(connection_t *);
+/** Convert a connection_t* to an edge_connection_t*; assert if the cast is
+ * invalid. */
+static edge_connection_t *TO_EDGE_CONN(connection_t *);
+/** Convert a connection_t* to an control_connection_t*; assert if the cast is
+ * invalid. */
+static control_connection_t *TO_CONTROL_CONN(connection_t *);
+
+static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
+{
+ tor_assert(c->magic == OR_CONNECTION_MAGIC);
+ return DOWNCAST(or_connection_t, c);
+}
+static INLINE dir_connection_t *TO_DIR_CONN(connection_t *c)
+{
+ tor_assert(c->magic == DIR_CONNECTION_MAGIC);
+ return DOWNCAST(dir_connection_t, c);
+}
+static INLINE edge_connection_t *TO_EDGE_CONN(connection_t *c)
+{
+ tor_assert(c->magic == EDGE_CONNECTION_MAGIC);
+ return DOWNCAST(edge_connection_t, c);
+}
+static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c)
+{
+ tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
+ return DOWNCAST(control_connection_t, c);
+}
typedef enum {
ADDR_POLICY_ACCEPT=1,
@@ -769,8 +853,13 @@ typedef struct cached_dir_t {
size_t dir_len; /**< Length of <b>dir</b> */
size_t dir_z_len; /**< Length of <b>dir_z</b> */
time_t published; /**< When was this object published */
+ int refcnt; /**< Reference count for this cached_dir_t. */
} cached_dir_t;
+typedef enum {
+ SAVED_NOWHERE=0, SAVED_IN_CACHE, SAVED_IN_JOURNAL
+} saved_location_t;
+
/** Information need to cache an onion router's descriptor. */
typedef struct signed_descriptor_t {
char *signed_descriptor_body;
@@ -778,6 +867,8 @@ typedef struct signed_descriptor_t {
char signed_descriptor_digest[DIGEST_LEN];
char identity_digest[DIGEST_LEN];
time_t published_on;
+ saved_location_t saved_location;
+ off_t saved_offset;
} signed_descriptor_t;
/** Information about another onion router in the network. */
@@ -809,6 +900,8 @@ typedef struct {
char *contact_info; /**< Declared contact info for this router. */
unsigned int is_hibernating:1; /**< Whether the router claims to be
* hibernating */
+ unsigned int has_old_dnsworkers:1; /**< Whether the router is using
+ * dnsworker code. */
/* local info */
unsigned int is_running:1; /**< As far as we know, is this OR currently
@@ -837,6 +930,10 @@ typedef struct {
/** How many times has a descriptor been posted and we believed
* this router to be unreachable? We only actually warn on the third. */
int num_unreachable_notifications;
+
+ /** What position is this descriptor within routerlist->routers? -1 for
+ * none. */
+ int routerlist_index;
} routerinfo_t;
/** Contents of a single router entry in a network status object.
@@ -940,6 +1037,8 @@ typedef struct {
/** List of signed_descriptor_t for older router descriptors we're
* caching. */
smartlist_t *old_routers;
+ /** DOCDOC */
+ tor_mmap_t *mmap_descriptors;
} routerlist_t;
/** Information on router used when extending a circuit. (We don't need a
@@ -1039,7 +1138,10 @@ typedef struct {
time_t expiry_time;
} cpath_build_state_t;
-#define CIRCUIT_MAGIC 0x35315243u
+#define ORIGIN_CIRCUIT_MAGIC 0x35315243u
+#define OR_CIRCUIT_MAGIC 0x98ABC04Fu
+
+typedef uint16_t circid_t;
/**
* A circuit is a path over the onion routing
@@ -1050,42 +1152,34 @@ typedef struct {
* OR connections multiplex many circuits at once, and stay standing even
* when there are no circuits running over them.
*
- * A circuit_t structure fills two roles. First, a circuit_t links two
- * connections together: either an edge connection and an OR connection,
- * or two OR connections. (When joined to an OR connection, a circuit_t
- * affects only cells sent to a particular circID on that connection. When
- * joined to an edge connection, a circuit_t affects all data.)
-
- * Second, a circuit_t holds the cipher keys and state for sending data
+ * A circuit_t structure cann fill one of two roles. First, a or_circuit_t
+ * links two connections together: either an edge connection and an OR
+ * connection, or two OR connections. (When joined to an OR connection, a
+ * circuit_t affects only cells sent to a particular circID on that
+ * connection. When joined to an edge connection, a circuit_t affects all
+ * data.)
+
+ * Second, an origin_circuit_t holds the cipher keys and state for sending data
* along a given circuit. At the OP, it has a sequence of ciphers, each
* of which is shared with a single OR along the circuit. Separate
* ciphers are used for data going "forward" (away from the OP) and
* "backward" (towards the OP). At the OR, a circuit has only two stream
* ciphers: one for data going forward, and one for data going backward.
*/
-struct circuit_t {
- uint32_t magic; /**< For memory debugging: must equal CIRCUIT_MAGIC. */
+typedef struct circuit_t {
+ uint32_t magic; /**< For memory and type debugging: must equal
+ * ORIGIN_CIRCUIT_MAGIC or OR_CIRCUIT_MAGIC. */
- /** The OR connection that is previous in this circuit. */
- connection_t *p_conn;
/** The OR connection that is next in this circuit. */
- connection_t *n_conn;
+ or_connection_t *n_conn;
/** The identity hash of n_conn. */
char n_conn_id_digest[DIGEST_LEN];
- /** Linked list of AP streams associated with this circuit. */
- connection_t *p_streams;
- /** Linked list of Exit streams associated with this circuit. */
- connection_t *n_streams;
- /** Linked list of Exit streams associated with this circuit that are
- * still being resolved. */
- connection_t *resolving_streams;
- /** The IPv4 address of the OR that is next in this circuit. */
- uint32_t n_addr;
+ /** The circuit_id used in the next (forward) hop of this circuit. */
+ uint16_t n_circ_id;
/** The port for the OR that is next in this circuit. */
uint16_t n_port;
- /** The next stream_id that will be tried when we're attempting to
- * construct a new AP stream originating at this circuit. */
- uint16_t next_stream_id;
+ /** The IPv4 address of the OR that is next in this circuit. */
+ uint32_t n_addr;
/** How many relay data cells can we package (read from edge streams)
* on this circuit before we receive a circuit-level sendme cell asking
* for more? */
@@ -1096,48 +1190,12 @@ struct circuit_t {
* more. */
int deliver_window;
- /** The circuit_id used in the previous (backward) hop of this circuit. */
- uint16_t p_circ_id;
- /** The circuit_id used in the next (forward) hop of this circuit. */
- uint16_t n_circ_id;
-
- /** The cipher used by intermediate hops for cells heading toward the
- * OP. */
- crypto_cipher_env_t *p_crypto;
- /** The cipher used by intermediate hops for cells heading away from
- * the OP. */
- crypto_cipher_env_t *n_crypto;
-
- /** The integrity-checking digest used by intermediate hops, for
- * cells packaged here and heading towards the OP.
- */
- crypto_digest_env_t *p_digest;
- /** The integrity-checking digest used by intermediate hops, for
- * cells packaged at the OP and arriving here.
- */
- crypto_digest_env_t *n_digest;
-
- /** Build state for this circuit. It includes the intended path
- * length, the chosen exit router, rendezvous information, etc.
- */
- cpath_build_state_t *build_state;
- /** The doubly-linked list of crypt_path_t entries, one per hop,
- * for this circuit. This includes ciphers for each hop,
- * integrity-checking digests for each hop, and package/delivery
- * windows for each hop.
- *
- * The cpath field is defined only when we are the circuit's origin.
- */
- crypt_path_t *cpath;
-
/** For storage while passing to cpuworker (state
* CIRCUIT_STATE_ONIONSKIN_PENDING), or while n_conn is pending
* (state CIRCUIT_STATE_OR_WAIT). When defined, it is always
* length ONIONSKIN_CHALLENGE_LEN. */
char *onionskin;
- char handshake_digest[DIGEST_LEN]; /**< Stores KH for intermediate hops. */
-
time_t timestamp_created; /**< When was this circuit created? */
time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the
* circuit is clean. */
@@ -1151,35 +1209,129 @@ struct circuit_t {
const char *marked_for_close_file; /**< For debugging: in which file was this
* circuit marked for close? */
- /**
- * The rend_query field holds the y portion of y.onion (nul-terminated)
- * if purpose is C_INTRODUCING or C_ESTABLISH_REND, or is a C_GENERAL
- * for a hidden service, or is S_*.
+ struct circuit_t *next; /**< Next circuit in linked list. */
+} circuit_t;
+
+/** An origin_circuit_t holds data necessary to build and use a circuit.
+ */
+typedef struct origin_circuit_t {
+ circuit_t _base;
+
+ /** Linked list of AP streams (or EXIT streams if hidden service)
+ * associated with this circuit. */
+ edge_connection_t *p_streams;
+ /** Build state for this circuit. It includes the intended path
+ * length, the chosen exit router, rendezvous information, etc.
*/
- char rend_query[REND_SERVICE_ID_LEN+1];
+ cpath_build_state_t *build_state;
+ /** The doubly-linked list of crypt_path_t entries, one per hop,
+ * for this circuit. This includes ciphers for each hop,
+ * integrity-checking digests for each hop, and package/delivery
+ * windows for each hop.
+ *
+ * The cpath field is defined only when we are the circuit's origin.
+ */
+ crypt_path_t *cpath;
/** The rend_pk_digest field holds a hash of location-hidden service's
- * PK if purpose is INTRO_POINT or S_ESTABLISH_INTRO or S_RENDEZVOUSING.
+ * PK if purpose is S_ESTABLISH_INTRO or S_RENDEZVOUSING.
*/
char rend_pk_digest[DIGEST_LEN];
- /** Holds rendezvous cookie if purpose is REND_POINT_WAITING or
- * C_ESTABLISH_REND. Filled with zeroes otherwise.
+ /** Holds rendezvous cookie if purpose is C_ESTABLISH_REND. Filled with
+ * zeroes otherwise.
*/
char rend_cookie[REND_COOKIE_LEN];
- /** Points to spliced circuit if purpose is REND_ESTABLISHED, and circuit
- * is not marked for close. */
- struct circuit_t *rend_splice;
+ /**
+ * The rend_query field holds the y portion of y.onion (nul-terminated)
+ * if purpose is C_INTRODUCING or C_ESTABLISH_REND, or is a C_GENERAL
+ * for a hidden service, or is S_*.
+ */
+ char rend_query[REND_SERVICE_ID_LEN+1];
+
+ /** The next stream_id that will be tried when we're attempting to
+ * construct a new AP stream originating at this circuit. */
+ uint16_t next_stream_id;
/** Quasi-global identifier for this circuit; used for control.c */
/* XXXX NM This can get re-used after 2**32 circuits. */
uint32_t global_identifier;
- struct circuit_t *next; /**< Next circuit in linked list. */
-};
+} origin_circuit_t;
+
+/** An or_circuit_t holds information needed to implement a circuit at an
+ * OR. */
+typedef struct or_circuit_t {
+ circuit_t _base;
+
+ /** The circuit_id used in the previous (backward) hop of this circuit. */
+ circid_t p_circ_id;
+ /** The OR connection that is previous in this circuit. */
+ or_connection_t *p_conn;
+ /** Linked list of Exit streams associated with this circuit. */
+ edge_connection_t *n_streams;
+ /** Linked list of Exit streams associated with this circuit that are
+ * still being resolved. */
+ edge_connection_t *resolving_streams;
+ /** The cipher used by intermediate hops for cells heading toward the
+ * OP. */
+ crypto_cipher_env_t *p_crypto;
+ /** The cipher used by intermediate hops for cells heading away from
+ * the OP. */
+ crypto_cipher_env_t *n_crypto;
+
+ /** The integrity-checking digest used by intermediate hops, for
+ * cells packaged here and heading towards the OP.
+ */
+ crypto_digest_env_t *p_digest;
+ /** The integrity-checking digest used by intermediate hops, for
+ * cells packaged at the OP and arriving here.
+ */
+ crypto_digest_env_t *n_digest;
+
+ /** Points to spliced circuit if purpose is REND_ESTABLISHED, and circuit
+ * is not marked for close. */
+ struct or_circuit_t *rend_splice;
+
+#if REND_COOKIE_LEN >= DIGEST_LEN
+#define REND_TOKEN_LEN REND_COOKIE_LEN
+#else
+#define REND_TOKEN_LEN DIGEST_LEN
+#endif
-typedef struct circuit_t circuit_t;
+ /** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a
+ * rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes
+ * otherwise.
+ */
+ char rend_token[REND_TOKEN_LEN];
+
+ char handshake_digest[DIGEST_LEN]; /**< Stores KH for the handshake. */
+} or_circuit_t;
+
+/** Convert a circuit subtype to a circuit_t.*/
+#define TO_CIRCUIT(x) (&((x)->_base))
+
+/** Convert a circuit_t* to a pointer to the enclosing or_circuit_t. Asserts
+ * if the cast is impossible. */
+static or_circuit_t *TO_OR_CIRCUIT(circuit_t *);
+/** Convert a circuit_t* to a pointer to the enclosing origin_circuit_t.
+ * Asserts if the cast is impossible. */
+static origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *);
+
+static INLINE or_circuit_t *TO_OR_CIRCUIT(circuit_t *x)
+{
+ tor_assert(x->magic == OR_CIRCUIT_MAGIC);
+ //return (or_circuit_t*) (((char*)x) - STRUCT_OFFSET(or_circuit_t, _base));
+ return DOWNCAST(or_circuit_t, x);
+}
+static INLINE origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x)
+{
+ tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC);
+ //return (origin_circuit_t*)
+ // (((char*)x) - STRUCT_OFFSET(origin_circuit_t, _base));
+ return DOWNCAST(origin_circuit_t, x);
+}
#define ALLOW_INVALID_ENTRY 1
#define ALLOW_INVALID_EXIT 2
@@ -1254,6 +1406,8 @@ typedef struct {
config_line_t *DirPolicy; /**< Lists of dir policy components */
/** Addresses to bind for listening for SOCKS connections. */
config_line_t *SocksListenAddress;
+ /** Addresses to bind for listening for transparent connections. */
+ config_line_t *TransListenAddress;
/** Addresses to bind for listening for OR connections. */
config_line_t *ORListenAddress;
/** Addresses to bind for listening for directory connections. */
@@ -1275,6 +1429,7 @@ typedef struct {
* length (alpha in geometric distribution). */
int ORPort; /**< Port to listen on for OR connections. */
int SocksPort; /**< Port to listen on for SOCKS connections. */
+ int TransPort; /**< Port to listen on for transparent connections. */
int ControlPort; /**< Port to listen on for control connections. */
int DirPort; /**< Port to listen on for directory connections. */
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
@@ -1286,6 +1441,8 @@ typedef struct {
int VersioningAuthoritativeDir; /**< Boolean: is this an authoritative
* directory that's willing to recommend
* versions? */
+ int AvoidDiskWrites; /**< Boolean: should we never cache things to disk?
+ * Not used yet. */
int ClientOnly; /**< Boolean: should we never evolve into a server role? */
int NoPublish; /**< Boolean: should we never publish a descriptor? */
int PublishServerDescriptor; /**< Do we publish our descriptor as normal? */
@@ -1314,12 +1471,8 @@ typedef struct {
int TrackHostExitsExpire; /**< Number of seconds until we expire an
* addressmap */
config_line_t *AddressMap; /**< List of address map directives. */
- int DirFetchPeriod; /**< How often do we fetch new directories? */
- int DirPostPeriod; /**< How often do we post our server descriptor to the
- * authoritative directory servers? */
int RendPostPeriod; /**< How often do we post each rendezvous service
* descriptor? Remember to publish them independently. */
- int StatusFetchPeriod; /**< How often do we fetch running-routers lists? */
int KeepalivePeriod; /**< How often do we send padding cells to keep
* connections alive? */
int SocksTimeout; /**< How long do we let a socks connection wait
@@ -1341,6 +1494,10 @@ typedef struct {
* to use in a second? */
uint64_t MaxAdvertisedBandwidth; /**< How much bandwidth are we willing to
* tell people we have? */
+ uint64_t RelayBandwidthRate; /**< How much bandwidth, on average, are we
+ * willing to use for all relayed conns? */
+ uint64_t RelayBandwidthBurst; /**< How much bandwidth, at maximum, will we
+ * use in a second for all relayed conns? */
int NumCpus; /**< How many CPUs should we try to use? */
int RunTesting; /**< If true, create testing circuits to measure how well the
* other ORs are running. */
@@ -1414,6 +1571,14 @@ typedef struct {
char *VirtualAddrNetwork; /**< Address and mask to hand out for virtual
* MAPADDRESS requests. */
+ int ServerDNSSearchDomains; /**< Boolean: If set, we don't force exit
+ * addresses to be FQDNs, but rather search for them in
+ * the local domains. */
+ int ServerDNSDetectHijacking; /**< Boolean: If true, check for DNS failure
+ * hijacking */
+ char *ServerDNSResolvConfFile; /**< If provided, we configure our internal
+ * resolver from the file here rather than from
+ * /etc/resolv.conf (unix) or the registry (windows) */
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -1447,6 +1612,7 @@ typedef struct {
#define MAX_SOCKS_ADDR_LEN 256
#define SOCKS_COMMAND_CONNECT 0x01
#define SOCKS_COMMAND_RESOLVE 0xF0
+#define SOCKS_COMMAND_RESOLVE_PTR 0xF1
/** State of a SOCKS request from a user to an OP */
struct socks_request_t {
char socks_version; /**< Which version of SOCKS did the client use? */
@@ -1485,6 +1651,8 @@ int flush_buf(int s, buf_t *buf, size_t sz, size_t *buf_flushlen);
int flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t sz, size_t *buf_flushlen);
int write_to_buf(const char *string, size_t string_len, buf_t *buf);
+int write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state,
+ const char *data, size_t data_len, int done);
int fetch_from_buf(char *string, size_t string_len, buf_t *buf);
int fetch_from_buf_http(buf_t *buf,
char **headers_out, size_t max_headerlen,
@@ -1500,32 +1668,34 @@ void assert_buf_ok(buf_t *buf);
/********************************* circuitbuild.c **********************/
-char *circuit_list_path(circuit_t *circ, int verbose);
-void circuit_log_path(int severity, unsigned int domain, circuit_t *circ);
-void circuit_rep_hist_note_result(circuit_t *circ);
-void circuit_dump_by_conn(connection_t *conn, int severity);
-circuit_t *circuit_init(uint8_t purpose, int need_uptime,
- int need_capacity, int internal);
-circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit,
+char *circuit_list_path(origin_circuit_t *circ, int verbose);
+void circuit_log_path(int severity, unsigned int domain,
+ origin_circuit_t *circ);
+void circuit_rep_hist_note_result(origin_circuit_t *circ);
+origin_circuit_t *origin_circuit_init(uint8_t purpose, int need_uptime,
+ int need_capacity, int internal);
+origin_circuit_t *circuit_establish_circuit(uint8_t purpose,
+ extend_info_t *exit,
int need_uptime, int need_capacity,
int internal);
-int circuit_handle_first_hop(circuit_t *circ);
-void circuit_n_conn_done(connection_t *or_conn, int status);
+int circuit_handle_first_hop(origin_circuit_t *circ);
+void circuit_n_conn_done(or_connection_t *or_conn, int status);
int inform_testing_reachability(void);
-int circuit_send_next_onion_skin(circuit_t *circ);
+int circuit_send_next_onion_skin(origin_circuit_t *circ);
void circuit_note_clock_jumped(int seconds_elapsed);
int circuit_extend(cell_t *cell, circuit_t *circ);
int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data,
int reverse);
-int circuit_finish_handshake(circuit_t *circ, uint8_t cell_type, char *reply);
-int circuit_truncated(circuit_t *circ, crypt_path_t *layer);
-int onionskin_answer(circuit_t *circ, uint8_t cell_type, char *payload,
+int circuit_finish_handshake(origin_circuit_t *circ, uint8_t cell_type,
+ char *reply);
+int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer);
+int onionskin_answer(or_circuit_t *circ, uint8_t cell_type, char *payload,
char *keys);
int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
int *need_capacity);
-int circuit_append_new_exit(circuit_t *circ, extend_info_t *info);
-int circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info);
+int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info);
+int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info);
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_from_router(routerinfo_t *r);
extend_info_t *extend_info_dup(extend_info_t *info);
@@ -1546,24 +1716,29 @@ void entry_guards_free_all(void);
circuit_t * _circuit_get_global_list(void);
const char *circuit_state_to_string(int state);
-enum which_conn_changed_t { P_CONN_CHANGED=1, N_CONN_CHANGED=0 };
-void circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
- connection_t *conn,
- enum which_conn_changed_t which);
+void circuit_dump_by_conn(connection_t *conn, int severity);
+void circuit_set_p_circid_orconn(or_circuit_t *circ, uint16_t id,
+ or_connection_t *conn);
+void circuit_set_n_circid_orconn(circuit_t *circ, uint16_t id,
+ or_connection_t *conn);
void circuit_set_state(circuit_t *circ, int state);
void circuit_close_all_marked(void);
-circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn);
-circuit_t *circuit_get_by_circid_orconn(uint16_t circ_id, connection_t *conn);
-int circuit_id_used_on_conn(uint16_t circ_id, connection_t *conn);
-circuit_t *circuit_get_by_edge_conn(connection_t *conn);
-void circuit_unlink_all_from_or_conn(connection_t *conn, int reason);
-circuit_t *circuit_get_by_global_id(uint32_t id);
-circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query,
- uint8_t purpose);
-circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start,
+origin_circuit_t *origin_circuit_new(void);
+or_circuit_t *or_circuit_new(uint16_t p_circ_id, or_connection_t *p_conn);
+circuit_t *circuit_get_by_circid_orconn(uint16_t circ_id,
+ or_connection_t *conn);
+int circuit_id_used_on_conn(uint16_t circ_id, or_connection_t *conn);
+circuit_t *circuit_get_by_edge_conn(edge_connection_t *conn);
+void circuit_unlink_all_from_or_conn(or_connection_t *conn, int reason);
+origin_circuit_t *circuit_get_by_global_id(uint32_t id);
+origin_circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query,
+ uint8_t purpose);
+origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const char *digest, uint8_t purpose);
-circuit_t *circuit_get_rendezvous(const char *cookie);
-circuit_t *circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
+or_circuit_t *circuit_get_rendezvous(const char *cookie);
+or_circuit_t *circuit_get_intro_point(const char *digest);
+origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
+ extend_info_t *info,
int need_uptime,
int need_capacity, int internal);
void circuit_mark_all_unused_circs(void);
@@ -1582,32 +1757,36 @@ void circuit_free_all(void);
void circuit_expire_building(time_t now);
void circuit_remove_handled_ports(smartlist_t *needed_ports);
-int circuit_stream_is_being_handled(connection_t *conn, uint16_t port,
+int circuit_stream_is_being_handled(edge_connection_t *conn, uint16_t port,
int min);
void circuit_build_needed_circs(time_t now);
-void circuit_detach_stream(circuit_t *circ, connection_t *conn);
+void circuit_detach_stream(circuit_t *circ, edge_connection_t *conn);
void circuit_about_to_close_connection(connection_t *conn);
-void circuit_has_opened(circuit_t *circ);
-void circuit_build_failed(circuit_t *circ);
-circuit_t *circuit_launch_by_nickname(uint8_t purpose,
+
+void reset_bandwidth_test(void);
+int circuit_enough_testing_circs(void);
+
+void circuit_has_opened(origin_circuit_t *circ);
+void circuit_build_failed(origin_circuit_t *circ);
+origin_circuit_t *circuit_launch_by_nickname(uint8_t purpose,
const char *exit_nickname,
int need_uptime, int need_capacity,
int is_internal);
-circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
+origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
extend_info_t *info,
int need_uptime, int need_capacity,
int is_internal);
-circuit_t *circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
+origin_circuit_t *circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
int need_uptime, int need_capacity,
int is_internal);
void circuit_reset_failure_count(int timeout);
-int connection_ap_handshake_attach_chosen_circuit(connection_t *conn,
- circuit_t *circ);
-int connection_ap_handshake_attach_circuit(connection_t *conn);
+int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
+ origin_circuit_t *circ);
+int connection_ap_handshake_attach_circuit(edge_connection_t *conn);
/********************************* command.c ***************************/
-void command_process_cell(cell_t *cell, connection_t *conn);
+void command_process_cell(cell_t *cell, or_connection_t *conn);
extern uint64_t stats_n_padding_cells_processed;
extern uint64_t stats_n_create_cells_processed;
@@ -1627,8 +1806,8 @@ int config_get_lines(char *string, config_line_t **result);
void config_free_lines(config_line_t *front);
int options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg);
-int resolve_my_address(or_options_t *options, uint32_t *addr,
- char **hostname_out);
+int resolve_my_address(int warn_severity, or_options_t *options,
+ uint32_t *addr, char **hostname_out);
void options_init(or_options_t *options);
int options_init_from_torrc(int argc, char **argv);
int options_init_logs(or_options_t *options, int validate_only);
@@ -1681,13 +1860,16 @@ int connection_fetch_from_buf(char *string, size_t len, connection_t *conn);
int connection_wants_to_flush(connection_t *conn);
int connection_outbuf_too_full(connection_t *conn);
int connection_handle_write(connection_t *conn);
-void _connection_controller_force_write(connection_t *conn);
+void _connection_controller_force_write(control_connection_t *conn);
void connection_write_to_buf(const char *string, size_t len,
connection_t *conn);
+void connection_write_to_buf_zlib(dir_connection_t *conn,
+ const char *data, size_t data_len,
+ int done);
-connection_t *connection_or_exact_get_by_addr_port(uint32_t addr,
+or_connection_t *connection_or_exact_get_by_addr_port(uint32_t addr,
uint16_t port);
-connection_t *connection_get_by_global_id(uint32_t id);
+edge_connection_t *connection_get_by_global_id(uint32_t id);
connection_t *connection_get_by_type(int type);
connection_t *connection_get_by_type_purpose(int type, int purpose);
@@ -1706,46 +1888,50 @@ int connection_state_is_connecting(connection_t *conn);
char *alloc_http_authenticator(const char *authenticator);
void assert_connection_ok(connection_t *conn, time_t now);
-int connection_or_nonopen_was_started_here(connection_t *conn);
+int connection_or_nonopen_was_started_here(or_connection_t *conn);
/********************************* connection_edge.c *************************/
#define connection_mark_unattached_ap(conn, endreason) \
_connection_mark_unattached_ap((conn), (endreason), __LINE__, _SHORT_FILE_)
-void _connection_mark_unattached_ap(connection_t *conn, int endreason,
+void _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
int line, const char *file);
-int connection_edge_reached_eof(connection_t *conn);
-int connection_edge_process_inbuf(connection_t *conn, int package_partial);
-int connection_edge_destroy(uint16_t circ_id, connection_t *conn);
-int connection_edge_end(connection_t *conn, char reason,
+int connection_edge_reached_eof(edge_connection_t *conn);
+int connection_edge_process_inbuf(edge_connection_t *conn,
+ int package_partial);
+int connection_edge_destroy(uint16_t circ_id, edge_connection_t *conn);
+int connection_edge_end(edge_connection_t *conn, char reason,
crypt_path_t *cpath_layer);
-int connection_edge_end_errno(connection_t *conn, crypt_path_t *cpath_layer);
-int connection_edge_finished_flushing(connection_t *conn);
-int connection_edge_finished_connecting(connection_t *conn);
+int connection_edge_end_errno(edge_connection_t *conn,
+ crypt_path_t *cpath_layer);
+int connection_edge_finished_flushing(edge_connection_t *conn);
+int connection_edge_finished_connecting(edge_connection_t *conn);
-int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ);
-int connection_ap_handshake_send_resolve(connection_t *ap_conn,
- circuit_t *circ);
+int connection_ap_handshake_send_begin(edge_connection_t *ap_conn,
+ origin_circuit_t *circ);
+int connection_ap_handshake_send_resolve(edge_connection_t *ap_conn,
+ origin_circuit_t *circ);
int connection_ap_make_bridge(char *address, uint16_t port);
-void connection_ap_handshake_socks_reply(connection_t *conn, char *reply,
+void connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
size_t replylen,
socks5_reply_status_t status);
-void connection_ap_handshake_socks_resolved(connection_t *conn,
+void connection_ap_handshake_socks_resolved(edge_connection_t *conn,
int answer_type,
size_t answer_len,
const char *answer,
int ttl);
int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
-int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ);
-void connection_exit_connect(connection_t *conn);
-int connection_edge_is_rendezvous_stream(connection_t *conn);
-int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit);
+int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
+void connection_exit_connect(edge_connection_t *conn);
+int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
+int connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit);
void connection_ap_expire_beginning(void);
void connection_ap_attach_pending(void);
-int connection_ap_detach_retriable(connection_t *conn, circuit_t *circ);
+int connection_ap_detach_retriable(edge_connection_t *conn,
+ origin_circuit_t *circ);
void addressmap_init(void);
void addressmap_clean(time_t now);
@@ -1753,7 +1939,7 @@ void addressmap_clear_configured(void);
void addressmap_clear_transient(void);
void addressmap_free_all(void);
void addressmap_rewrite(char *address, size_t maxlen);
-int addressmap_already_mapped(const char *address);
+int addressmap_have_mapping(const char *address);
void addressmap_register(const char *address, char *new_address,
time_t expires);
int parse_virtual_addr_network(const char *val, int validate_only,
@@ -1766,8 +1952,8 @@ int address_is_in_virtual_range(const char *addr);
const char *addressmap_register_virtual_address(int type, char *new_address);
void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
time_t max_expires);
-int connection_ap_handshake_rewrite_and_attach(connection_t *conn,
- circuit_t *circ);
+int connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
+ origin_circuit_t *circ);
void set_exit_redirects(smartlist_t *lst);
typedef enum hostname_type_t {
@@ -1777,23 +1963,24 @@ hostname_type_t parse_extended_hostname(char *address);
/********************************* connection_or.c ***************************/
-void connection_or_remove_from_identity_map(connection_t *conn);
+void connection_or_remove_from_identity_map(or_connection_t *conn);
void connection_or_clear_identity_map(void);
-connection_t *connection_or_get_by_identity_digest(const char *digest);
+or_connection_t *connection_or_get_by_identity_digest(const char *digest);
-int connection_or_reached_eof(connection_t *conn);
-int connection_or_process_inbuf(connection_t *conn);
-int connection_or_finished_flushing(connection_t *conn);
-int connection_or_finished_connecting(connection_t *conn);
+int connection_or_reached_eof(or_connection_t *conn);
+int connection_or_process_inbuf(or_connection_t *conn);
+int connection_or_finished_flushing(or_connection_t *conn);
+int connection_or_finished_connecting(or_connection_t *conn);
-connection_t *connection_or_connect(uint32_t addr, uint16_t port,
+or_connection_t *connection_or_connect(uint32_t addr, uint16_t port,
const char *id_digest);
-int connection_tls_start_handshake(connection_t *conn, int receiving);
-int connection_tls_continue_handshake(connection_t *conn);
+int connection_tls_start_handshake(or_connection_t *conn, int receiving);
+int connection_tls_continue_handshake(or_connection_t *conn);
-void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn);
-int connection_or_send_destroy(uint16_t circ_id, connection_t *conn,
+void connection_or_write_cell_to_buf(const cell_t *cell,
+ or_connection_t *conn);
+int connection_or_send_destroy(uint16_t circ_id, or_connection_t *conn,
int reason);
/********************************* control.c ***************************/
@@ -1852,13 +2039,16 @@ void control_adjust_event_log_severity(void);
#define LOG_FN_CONN(conn, args) \
CONN_LOG_PROTECT(conn, log_fn args)
-int connection_control_finished_flushing(connection_t *conn);
-int connection_control_reached_eof(connection_t *conn);
-int connection_control_process_inbuf(connection_t *conn);
+int connection_control_finished_flushing(control_connection_t *conn);
+int connection_control_reached_eof(control_connection_t *conn);
+int connection_control_process_inbuf(control_connection_t *conn);
-int control_event_circuit_status(circuit_t *circ, circuit_status_event_t e);
-int control_event_stream_status(connection_t *conn, stream_status_event_t e);
-int control_event_or_conn_status(connection_t *conn, or_conn_status_event_t e);
+int control_event_circuit_status(origin_circuit_t *circ,
+ circuit_status_event_t e);
+int control_event_stream_status(edge_connection_t *conn,
+ stream_status_event_t e);
+int control_event_or_conn_status(or_connection_t *conn,
+ or_conn_status_event_t e);
int control_event_bandwidth_used(uint32_t n_read, uint32_t n_written);
void control_event_logmsg(int severity, unsigned int domain, const char *msg);
int control_event_descriptors_changed(smartlist_t *routers);
@@ -1904,20 +2094,21 @@ void directory_initiate_command_routerstatus(routerstatus_t *status,
int parse_http_response(const char *headers, int *code, time_t *date,
int *compression, char **response);
-int connection_dir_reached_eof(connection_t *conn);
-int connection_dir_process_inbuf(connection_t *conn);
-int connection_dir_finished_flushing(connection_t *conn);
-int connection_dir_finished_connecting(connection_t *conn);
-void connection_dir_request_failed(connection_t *conn);
+int connection_dir_reached_eof(dir_connection_t *conn);
+int connection_dir_process_inbuf(dir_connection_t *conn);
+int connection_dir_finished_flushing(dir_connection_t *conn);
+int connection_dir_finished_connecting(dir_connection_t *conn);
+void connection_dir_request_failed(dir_connection_t *conn);
int dir_split_resource_into_fingerprints(const char *resource,
smartlist_t *fp_out, int *compresseed_out,
- int decode_hex);
+ int decode_hex, int sort_uniq);
char *directory_dump_request_log(void);
/********************************* dirserv.c ***************************/
+int connection_dirserv_flushed_some(dir_connection_t *conn);
int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk);
-int dirserv_parse_fingerprint_file(const char *fname);
+int dirserv_load_fingerprint_file(void);
void dirserv_free_fingerprint_list(void);
const char *dirserv_get_nickname_by_digest(const char *digest);
int dirserv_add_descriptor(const char *desc, const char **msg);
@@ -1927,9 +2118,10 @@ int dirserv_thinks_router_is_blatantly_unreachable(routerinfo_t *router,
time_t now);
int list_server_status(smartlist_t *routers, char **router_status_out);
int dirserv_dump_directory_to_string(char **dir_out,
- crypto_pk_env_t *private_key);
+ crypto_pk_env_t *private_key,
+ int complete);
void directory_set_dirty(void);
-size_t dirserv_get_directory(const char **cp, int compress);
+cached_dir_t *dirserv_get_directory(void);
size_t dirserv_get_runningrouters(const char **rr, int compress);
void dirserv_set_cached_directory(const char *directory, time_t when,
int is_running_routers);
@@ -1937,6 +2129,10 @@ void dirserv_set_cached_networkstatus_v2(const char *directory,
const char *identity,
time_t published);
void dirserv_get_networkstatus_v2(smartlist_t *result, const char *key);
+void dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
+ const char *key);
+int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
+ const char **msg);
int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
const char **msg);
void dirserv_orconn_tls_done(const char *address,
@@ -1944,24 +2140,28 @@ void dirserv_orconn_tls_done(const char *address,
const char *digest_rcvd,
const char *nickname,
int as_advertised);
+void dirserv_test_reachability(int try_all);
int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
int complain);
int dirserv_would_reject_router(routerstatus_t *rs);
void dirserv_free_all(void);
+void cached_dir_decref(cached_dir_t *d);
/********************************* dns.c ***************************/
-void dns_init(void);
+int dns_init(void);
void dns_free_all(void);
+uint32_t dns_clip_ttl(uint32_t ttl);
int connection_dns_finished_flushing(connection_t *conn);
int connection_dns_reached_eof(connection_t *conn);
int connection_dns_process_inbuf(connection_t *conn);
-void dnsworkers_rotate(void);
-void connection_dns_remove(connection_t *conn);
-void assert_connection_edge_not_dns_pending(connection_t *conn);
+void dns_reset(void);
+void connection_dns_remove(edge_connection_t *conn);
+void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
void assert_all_pending_dns_resolves_ok(void);
-void dns_cancel_pending_resolve(char *question);
-int dns_resolve(connection_t *exitconn);
+void dns_cancel_pending_resolve(const char *question);
+int dns_resolve(edge_connection_t *exitconn);
+void dns_launch_wildcard_checks(void);
/********************************* hibernate.c **********************/
@@ -1979,6 +2179,8 @@ void accounting_set_bandwidth_usage_from_state(or_state_t *state);
/********************************* main.c ***************************/
+extern int has_completed_circuit;
+
int connection_add(connection_t *conn);
int connection_remove(connection_t *conn);
int connection_in_array(connection_t *conn);
@@ -1999,7 +2201,8 @@ void connection_start_writing(connection_t *conn);
void directory_all_unreachable(time_t now);
void directory_info_has_arrived(time_t now, int from_cache);
-int control_signal_act(int the_signal);
+int control_signal_check(int the_signal);
+void control_signal_act(int the_signal);
void handle_signals(int is_parent);
void tor_cleanup(void);
void tor_free_all(int postfork);
@@ -2008,9 +2211,9 @@ int tor_main(int argc, char *argv[]);
/********************************* onion.c ***************************/
-int onion_pending_add(circuit_t *circ);
-circuit_t *onion_next_task(void);
-void onion_pending_remove(circuit_t *circ);
+int onion_pending_add(or_circuit_t *circ);
+or_circuit_t *onion_next_task(void);
+void onion_pending_remove(or_circuit_t *circ);
int onion_skin_create(crypto_pk_env_t *router_key,
crypto_dh_env_t **handshake_state_out,
@@ -2067,6 +2270,8 @@ int policies_parse_exit_policy(config_line_t *cfg,
addr_policy_t **dest,
int rejectprivate);
int exit_policy_is_general_exit(addr_policy_t *policy);
+int policy_is_reject_star(addr_policy_t *policy);
+int policies_getinfo_helper(const char *question, char **answer);
void addr_policy_free(addr_policy_t *p);
void policies_free_all(void);
@@ -2081,12 +2286,13 @@ int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
void relay_header_pack(char *dest, const relay_header_t *src);
void relay_header_unpack(relay_header_t *dest, const char *src);
-int connection_edge_send_command(connection_t *fromconn, circuit_t *circ,
+int connection_edge_send_command(edge_connection_t *fromconn, circuit_t *circ,
int relay_command, const char *payload,
size_t payload_len,
crypt_path_t *cpath_layer);
-int connection_edge_package_raw_inbuf(connection_t *conn, int package_partial);
-void connection_edge_consider_sending_sendme(connection_t *conn);
+int connection_edge_package_raw_inbuf(edge_connection_t *conn,
+ int package_partial);
+void connection_edge_consider_sending_sendme(edge_connection_t *conn);
socks5_reply_status_t connection_edge_end_reason_socks5_response(int reason);
int errno_to_end_reason(int e);
@@ -2110,6 +2316,8 @@ void rep_hist_note_bytes_read(int num_bytes, time_t when);
void rep_hist_note_bytes_written(int num_bytes, time_t when);
int rep_hist_bandwidth_assess(void);
char *rep_hist_get_bandwidth_lines(void);
+void rep_hist_update_state(or_state_t *state);
+int rep_hist_load_state(or_state_t *state, char **err);
void rep_history_clean(time_t before);
void rep_hist_note_used_port(uint16_t port, time_t now);
@@ -2120,8 +2328,8 @@ void rep_hist_note_used_internal(time_t now, int need_uptime,
int rep_hist_get_predicted_internal(time_t now, int *need_uptime,
int *need_capacity);
-void rep_hist_update_state(or_state_t *state);
-int rep_hist_load_state(or_state_t *state, char **err);
+int any_predicted_circuits(time_t now);
+int rep_hist_circbuilding_dormant(time_t now);
int any_predicted_circuits(time_t now);
@@ -2129,22 +2337,23 @@ void rep_hist_free_all(void);
/********************************* rendclient.c ***************************/
-void rend_client_introcirc_has_opened(circuit_t *circ);
-void rend_client_rendcirc_has_opened(circuit_t *circ);
-int rend_client_introduction_acked(circuit_t *circ, const char *request,
+void rend_client_introcirc_has_opened(origin_circuit_t *circ);
+void rend_client_rendcirc_has_opened(origin_circuit_t *circ);
+int rend_client_introduction_acked(origin_circuit_t *circ, const char *request,
size_t request_len);
void rend_client_refetch_renddesc(const char *query);
int rend_client_remove_intro_point(extend_info_t *failed_intro,
const char *query);
-int rend_client_rendezvous_acked(circuit_t *circ, const char *request,
+int rend_client_rendezvous_acked(origin_circuit_t *circ, const char *request,
size_t request_len);
-int rend_client_receive_rendezvous(circuit_t *circ, const char *request,
+int rend_client_receive_rendezvous(origin_circuit_t *circ, const char *request,
size_t request_len);
void rend_client_desc_here(const char *query);
extend_info_t *rend_client_get_random_intro(const char *query);
-int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc);
+int rend_client_send_introduction(origin_circuit_t *introcirc,
+ origin_circuit_t *rendcirc);
/********************************* rendcommon.c ***************************/
@@ -2209,25 +2418,27 @@ void rend_services_init(void);
void rend_services_introduce(void);
void rend_consider_services_upload(time_t now);
-void rend_service_intro_has_opened(circuit_t *circuit);
-int rend_service_intro_established(circuit_t *circuit, const char *request,
+void rend_service_intro_has_opened(origin_circuit_t *circuit);
+int rend_service_intro_established(origin_circuit_t *circuit,
+ const char *request,
size_t request_len);
-void rend_service_rendezvous_has_opened(circuit_t *circuit);
-int rend_service_introduce(circuit_t *circuit, const char *request,
+void rend_service_rendezvous_has_opened(origin_circuit_t *circuit);
+int rend_service_introduce(origin_circuit_t *circuit, const char *request,
size_t request_len);
-void rend_service_relaunch_rendezvous(circuit_t *oldcirc);
-int rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ);
+void rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc);
+int rend_service_set_connection_addr_port(edge_connection_t *conn,
+ origin_circuit_t *circ);
void rend_service_dump_stats(int severity);
void rend_service_free_all(void);
/********************************* rendmid.c *******************************/
-int rend_mid_establish_intro(circuit_t *circ, const char *request,
+int rend_mid_establish_intro(or_circuit_t *circ, const char *request,
size_t request_len);
-int rend_mid_introduce(circuit_t *circ, const char *request,
+int rend_mid_introduce(or_circuit_t *circ, const char *request,
size_t request_len);
-int rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
+int rend_mid_establish_rendezvous(or_circuit_t *circ, const char *request,
size_t request_len);
-int rend_mid_rendezvous(circuit_t *circ, const char *request,
+int rend_mid_rendezvous(or_circuit_t *circ, const char *request,
size_t request_len);
/********************************* router.c ***************************/
@@ -2245,31 +2456,33 @@ int init_keys(void);
int check_whether_orport_reachable(void);
int check_whether_dirport_reachable(void);
-void consider_testing_reachability(void);
+void consider_testing_reachability(int test_or, int test_dir);
void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void server_has_changed_ip(void);
-void consider_publishable_server(time_t now, int force);
+void router_perform_bandwidth_test(int num_circs, time_t now);
int authdir_mode(or_options_t *options);
int clique_mode(or_options_t *options);
int server_mode(or_options_t *options);
int advertised_server_mode(void);
int proxy_mode(or_options_t *options);
+void consider_publishable_server(int force);
-void router_retry_connections(int testing_reachability, int try_all);
int router_is_clique_mode(routerinfo_t *router);
void router_upload_dir_desc_to_dirservers(int force);
void mark_my_descriptor_dirty_if_older_than(time_t when);
void mark_my_descriptor_dirty(void);
void check_descriptor_bandwidth_changed(time_t now);
void check_descriptor_ipaddress_changed(time_t now);
-int router_compare_to_my_exit_policy(connection_t *conn);
+void router_new_address_suggestion(const char *suggestion);
+int router_compare_to_my_exit_policy(edge_connection_t *conn);
routerinfo_t *router_get_my_routerinfo(void);
const char *router_get_my_descriptor(void);
int router_digest_is_me(const char *digest);
int router_is_me(routerinfo_t *router);
int router_fingerprint_is_me(const char *fp);
+int router_pick_published_address(or_options_t *options, uint32_t *addr);
int router_rebuild_descriptor(int force);
int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
crypto_pk_env_t *ident_key);
@@ -2360,10 +2573,11 @@ int router_add_to_routerlist(routerinfo_t *router, const char **msg,
int from_cache, int from_fetch);
int router_load_single_router(const char *s, uint8_t purpose,
const char **msg);
-void router_load_routers_from_string(const char *s, int from_cache,
+void router_load_routers_from_string(const char *s,
+ saved_location_t saved_location,
smartlist_t *requested_fingerprints);
typedef enum {
- NS_FROM_CACHE, NS_FROM_DIR, NS_GENERATED
+ NS_FROM_CACHE, NS_FROM_DIR_BY_FP, NS_FROM_DIR_ALL, NS_GENERATED
} networkstatus_source_t;
int router_set_networkstatus(const char *s, time_t arrived_at,
networkstatus_source_t source,
@@ -2409,9 +2623,6 @@ typedef struct tor_version_t {
* VER_RELEASE. */
enum { VER_PRE=0, VER_RC=1, VER_RELEASE=2, } status;
int patchlevel;
- /** CVS status. For version in the post-0.1 format, this is always
- * IS_NOT_CVS */
- enum { IS_CVS=0, IS_NOT_CVS=1} cvs;
char status_tag[MAX_STATUS_TAG_LEN];
} tor_version_t;
@@ -2432,7 +2643,8 @@ int router_append_dirobj_signature(char *buf, size_t buf_len,
const char *digest,
crypto_pk_env_t *private_key);
int router_parse_list_from_string(const char **s,
- smartlist_t *dest);
+ smartlist_t *dest,
+ saved_location_t saved_location);
int router_parse_routerlist_from_directory(const char *s,
routerlist_t **dest,
crypto_pk_env_t *pkey,
@@ -2440,7 +2652,8 @@ int router_parse_routerlist_from_directory(const char *s,
int write_to_cache);
int router_parse_runningrouters(const char *str);
int router_parse_directory(const char *str);
-routerinfo_t *router_parse_entry_from_string(const char *s, const char *end);
+routerinfo_t *router_parse_entry_from_string(const char *s, const char *end,
+ int cache_copy);
addr_policy_t *router_parse_addr_policy_from_string(const char *s,
int assume_action);
version_status_t tor_version_is_obsolete(const char *myversion,
diff --git a/src/or/policies.c b/src/or/policies.c
index b7946f9204..327af18d62 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -128,7 +128,7 @@ parse_reachable_addresses(void)
int
firewall_is_fascist_or(void)
{
- return !!reachable_or_addr_policy;
+ return reachable_or_addr_policy != NULL;
}
/** Return true iff <b>policy</b> (possibly NULL) will allow a
@@ -603,7 +603,7 @@ policies_parse_exit_policy(config_line_t *cfg, addr_policy_t **dest,
/** Return true iff <b>ri</b> is "useful as an exit node", meaning
* it allows exit to at least one /8 address space for at least
- * one of ports 80, 443, and 6667. */
+ * two of ports 80, 443, and 6667. */
int
exit_policy_is_general_exit(addr_policy_t *policy)
{
@@ -619,10 +619,40 @@ exit_policy_is_general_exit(addr_policy_t *policy)
if ((p->addr & 0xff000000ul) == 0x7f000000ul)
continue; /* 127.x */
/* We have a match that is at least a /8. */
- if (p->policy_type == ADDR_POLICY_ACCEPT)
- return 1;
+ if (p->policy_type == ADDR_POLICY_ACCEPT) {
+ ++n_allowed;
+ break; /* stop considering this port */
+ }
}
}
+ return n_allowed >= 2;
+}
+
+/** Return false if <b>policy</b> might permit access to some addr:port;
+ * otherwise if we are certain it rejects everything, return true. */
+int
+policy_is_reject_star(addr_policy_t *p)
+{
+ for ( ; p; p = p->next) {
+ if (p->policy_type == ADDR_POLICY_ACCEPT)
+ return 0;
+ else if (p->policy_type == ADDR_POLICY_REJECT &&
+ p->prt_min <= 1 && p->prt_max == 65535 &&
+ p->msk == 0)
+ return 1;
+ }
+ return 1;
+}
+
+int
+policies_getinfo_helper(const char *question, char **answer)
+{
+ if (!strcmp(question, "exit-policy/default")) {
+ *answer = tor_strdup(DEFAULT_EXIT_POLICY);
+// } else if (!strcmp(question, "exit-policy/prepend")) {
+ } else {
+ *answer = NULL;
+ }
return 0;
}
diff --git a/src/or/relay.c b/src/or/relay.c
index 8dbe4acc12..8d9ce04fb1 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -16,19 +16,19 @@ const char relay_c_id[] =
static int relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
crypt_path_t **layer_hint, char *recognized);
-static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
+static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
int cell_direction);
static int
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
- connection_t *conn,
+ edge_connection_t *conn,
crypt_path_t *layer_hint);
static void
circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint);
static void
circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint);
static int
-circuit_resume_edge_reading_helper(connection_t *conn,
+circuit_resume_edge_reading_helper(edge_connection_t *conn,
circuit_t *circ,
crypt_path_t *layer_hint);
static int
@@ -144,7 +144,7 @@ relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
int
circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
{
- connection_t *conn=NULL;
+ or_connection_t *or_conn=NULL;
crypt_path_t *layer_hint=NULL;
char recognized=0;
int reason;
@@ -162,7 +162,7 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
}
if (recognized) {
- conn = relay_lookup_conn(circ, cell, cell_direction);
+ edge_connection_t *conn = relay_lookup_conn(circ, cell, cell_direction);
if (cell_direction == CELL_DIRECTION_OUT) {
++stats_n_relay_cells_delivered;
log_debug(LD_OR,"Sending away from origin.");
@@ -190,19 +190,25 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
/* not recognized. pass it on. */
if (cell_direction == CELL_DIRECTION_OUT) {
cell->circ_id = circ->n_circ_id; /* switch it */
- conn = circ->n_conn;
+ or_conn = circ->n_conn;
+ } else if (! CIRCUIT_IS_ORIGIN(circ)) {
+ cell->circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; /* switch it */
+ or_conn = TO_OR_CIRCUIT(circ)->p_conn;
} else {
- cell->circ_id = circ->p_circ_id; /* switch it */
- conn = circ->p_conn;
+ // XXXX NM WARN.
+ return 0;
}
- if (!conn) {
- if (circ->rend_splice && cell_direction == CELL_DIRECTION_OUT) {
+ if (!or_conn) {
+ // XXXX Can this splice stuff be done more cleanly?
+ if (! CIRCUIT_IS_ORIGIN(circ) &&
+ TO_OR_CIRCUIT(circ)->rend_splice &&
+ cell_direction == CELL_DIRECTION_OUT) {
+ or_circuit_t *splice = TO_OR_CIRCUIT(circ)->rend_splice;
tor_assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
- tor_assert(circ->rend_splice->purpose ==
- CIRCUIT_PURPOSE_REND_ESTABLISHED);
- cell->circ_id = circ->rend_splice->p_circ_id;
- if ((reason = circuit_receive_relay_cell(cell, circ->rend_splice,
+ tor_assert(splice->_base.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
+ cell->circ_id = splice->p_circ_id;
+ if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice),
CELL_DIRECTION_IN)) < 0) {
log_warn(LD_REND, "Error relaying cell across rendezvous; closing "
"circuits");
@@ -219,7 +225,7 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
log_debug(LD_OR,"Passing on unrecognized cell.");
++stats_n_relay_cells_relayed;
- connection_or_write_cell_to_buf(cell, conn);
+ connection_or_write_cell_to_buf(cell, or_conn);
return 0;
}
@@ -240,12 +246,10 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction)
* Return -1 to indicate that we should mark the circuit for close,
* else return 0.
*/
-/* wrap this into receive_relay_cell one day */
static int
relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
crypt_path_t **layer_hint, char *recognized)
{
- crypt_path_t *thishop;
relay_header_t rh;
tor_assert(circ);
@@ -257,8 +261,8 @@ relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
if (cell_direction == CELL_DIRECTION_IN) {
if (CIRCUIT_IS_ORIGIN(circ)) { /* We're at the beginning of the circuit.
* We'll want to do layered decrypts. */
- tor_assert(circ->cpath);
- thishop = circ->cpath;
+ crypt_path_t *thishop, *cpath = TO_ORIGIN_CIRCUIT(circ)->cpath;
+ thishop = cpath;
if (thishop->state != CPATH_STATE_OPEN) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Relay cell before first created cell? Closing.");
@@ -281,25 +285,27 @@ relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
}
thishop = thishop->next;
- } while (thishop != circ->cpath && thishop->state == CPATH_STATE_OPEN);
- log_warn(LD_OR,"in-cell at OP not recognized. Closing.");
+ } while (thishop != cpath && thishop->state == CPATH_STATE_OPEN);
+ log_warn(LD_OR,"in-cell at client not recognized. Closing.");
return -1;
} else { /* we're in the middle. Just one crypt. */
- if (relay_crypt_one_payload(circ->p_crypto, cell->payload, 1) < 0)
+ if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->p_crypto,
+ cell->payload, 1) < 0)
return -1;
// log_fn(LOG_DEBUG,"Skipping recognized check, because we're not "
-// "the OP.");
+// "the client.");
}
} else /* cell_direction == CELL_DIRECTION_OUT */ {
/* we're in the middle. Just one crypt. */
- if (relay_crypt_one_payload(circ->n_crypto, cell->payload, 0) < 0)
+ if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->n_crypto,
+ cell->payload, 0) < 0)
return -1;
relay_header_unpack(&rh, cell->payload);
if (rh.recognized == 0) {
/* it's possibly recognized. have to check digest to be sure. */
- if (relay_digest_matches(circ->n_digest, cell)) {
+ if (relay_digest_matches(TO_OR_CIRCUIT(circ)->n_digest, cell)) {
*recognized = 1;
return 0;
}
@@ -308,7 +314,7 @@ relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
return 0;
}
-/** Package a relay cell:
+/** Package a relay cell from an edge:
* - Encrypt it to the right layer
* - connection_or_write_cell_to_buf to the right conn
*/
@@ -317,12 +323,12 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
int cell_direction,
crypt_path_t *layer_hint)
{
- connection_t *conn; /* where to send the cell */
- crypt_path_t *thishop; /* counter for repeated crypts */
+ or_connection_t *conn; /* where to send the cell */
if (cell_direction == CELL_DIRECTION_OUT) {
+ crypt_path_t *thishop; /* counter for repeated crypts */
conn = circ->n_conn;
- if (!conn) {
+ if (!CIRCUIT_IS_ORIGIN(circ) || !conn) {
log_warn(LD_BUG,"outgoing relay cell has n_conn==NULL. Dropping.");
return 0; /* just drop it */
}
@@ -339,18 +345,20 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
}
thishop = thishop->prev;
- } while (thishop != circ->cpath->prev);
+ } while (thishop != TO_ORIGIN_CIRCUIT(circ)->cpath->prev);
} else { /* incoming cell */
- conn = circ->p_conn;
- if (!conn) {
+ or_circuit_t *or_circ;
+ if (CIRCUIT_IS_ORIGIN(circ)) {
/* XXXX RD This is a bug, right? */
- log_warn(LD_BUG,"incoming relay cell has p_conn==NULL. Dropping.");
+ log_warn(LD_BUG,"incoming relay cell at origin circuit. Dropping.");
assert_circuit_ok(circ);
return 0; /* just drop it */
}
- relay_set_digest(circ->p_digest, cell);
- if (relay_crypt_one_payload(circ->p_crypto, cell->payload, 1) < 0)
+ or_circ = TO_OR_CIRCUIT(circ);
+ conn = or_circ->p_conn;
+ relay_set_digest(or_circ->p_digest, cell);
+ if (relay_crypt_one_payload(or_circ->p_crypto, cell->payload, 1) < 0)
return -1;
}
++stats_n_relay_cells_relayed;
@@ -361,10 +369,10 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
/** If cell's stream_id matches the stream_id of any conn that's
* attached to circ, return that conn, else return NULL.
*/
-static connection_t *
+static edge_connection_t *
relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction)
{
- connection_t *tmpconn;
+ edge_connection_t *tmpconn;
relay_header_t rh;
relay_header_unpack(&rh, cell->payload);
@@ -376,25 +384,33 @@ relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction)
* that we allow rendezvous *to* an OP.
*/
- for (tmpconn = circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) {
- if (rh.stream_id == tmpconn->stream_id && !tmpconn->marked_for_close) {
- log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
- if (cell_direction == CELL_DIRECTION_OUT ||
- connection_edge_is_rendezvous_stream(tmpconn))
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ for (tmpconn = TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn;
+ tmpconn=tmpconn->next_stream) {
+ if (rh.stream_id == tmpconn->stream_id &&
+ !tmpconn->_base.marked_for_close) {
+ log_debug(LD_APP,"found conn for stream %d.", rh.stream_id);
return tmpconn;
+ }
}
- }
- for (tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) {
- if (rh.stream_id == tmpconn->stream_id && !tmpconn->marked_for_close) {
- log_debug(LD_APP,"found conn for stream %d.", rh.stream_id);
- return tmpconn;
+ } else {
+ for (tmpconn = TO_OR_CIRCUIT(circ)->n_streams; tmpconn;
+ tmpconn=tmpconn->next_stream) {
+ if (rh.stream_id == tmpconn->stream_id &&
+ !tmpconn->_base.marked_for_close) {
+ log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
+ if (cell_direction == CELL_DIRECTION_OUT ||
+ connection_edge_is_rendezvous_stream(tmpconn))
+ return tmpconn;
+ }
}
- }
- for (tmpconn = circ->resolving_streams; tmpconn;
- tmpconn=tmpconn->next_stream) {
- if (rh.stream_id == tmpconn->stream_id && !tmpconn->marked_for_close) {
- log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
- return tmpconn;
+ for (tmpconn = TO_OR_CIRCUIT(circ)->resolving_streams; tmpconn;
+ tmpconn=tmpconn->next_stream) {
+ if (rh.stream_id == tmpconn->stream_id &&
+ !tmpconn->_base.marked_for_close) {
+ log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
+ return tmpconn;
+ }
}
}
return NULL; /* probably a begin relay cell */
@@ -439,30 +455,32 @@ relay_header_unpack(relay_header_t *dest, const char *src)
* return -1. Else return 0.
*/
int
-connection_edge_send_command(connection_t *fromconn, circuit_t *circ,
+connection_edge_send_command(edge_connection_t *fromconn, circuit_t *circ,
int relay_command, const char *payload,
size_t payload_len, crypt_path_t *cpath_layer)
{
cell_t cell;
relay_header_t rh;
int cell_direction;
+ /* XXXX NM Split this function into a separate versions per circuit type? */
- if (fromconn && fromconn->marked_for_close) {
+ if (fromconn && fromconn->_base.marked_for_close) {
log_warn(LD_BUG,
"Bug: called on conn that's already marked for close at %s:%d.",
- fromconn->marked_for_close_file, fromconn->marked_for_close);
+ fromconn->_base.marked_for_close_file,
+ fromconn->_base.marked_for_close);
return 0;
}
if (!circ) {
tor_assert(fromconn);
- if (fromconn->type == CONN_TYPE_AP) {
+ if (fromconn->_base.type == CONN_TYPE_AP) {
log_info(LD_APP,"no circ. Closing conn.");
connection_mark_unattached_ap(fromconn, END_STREAM_REASON_INTERNAL);
} else {
log_info(LD_EXIT,"no circ. Closing conn.");
- fromconn->has_sent_end = 1; /* no circ to send to */
- connection_mark_for_close(fromconn);
+ fromconn->_base.edge_has_sent_end = 1; /* no circ to send to */
+ connection_mark_for_close(TO_CONN(fromconn));
}
return -1;
}
@@ -472,9 +490,11 @@ connection_edge_send_command(connection_t *fromconn, circuit_t *circ,
if (cpath_layer) {
cell.circ_id = circ->n_circ_id;
cell_direction = CELL_DIRECTION_OUT;
- } else {
- cell.circ_id = circ->p_circ_id;
+ } else if (! CIRCUIT_IS_ORIGIN(circ)) {
+ cell.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
cell_direction = CELL_DIRECTION_IN;
+ } else {
+ return -1;
}
memset(&rh, 0, sizeof(rh));
@@ -619,8 +639,8 @@ errno_to_end_reason(int e)
E_CASE(EMFILE):
return END_STREAM_REASON_RESOURCELIMIT;
default:
- log_info(LD_EXIT, "Didn't recognize errno %d (%s); telling the OP that "
- "we are ending a stream for 'misc' reason.",
+ log_info(LD_EXIT, "Didn't recognize errno %d (%s); telling the client "
+ "that we are ending a stream for 'misc' reason.",
e, tor_socket_strerror(e));
return END_STREAM_REASON_MISC;
}
@@ -648,15 +668,16 @@ edge_reason_is_retriable(int reason)
*/
static int
connection_edge_process_end_not_open(
- relay_header_t *rh, cell_t *cell, circuit_t *circ,
- connection_t *conn, crypt_path_t *layer_hint)
+ relay_header_t *rh, cell_t *cell, origin_circuit_t *circ,
+ edge_connection_t *conn, crypt_path_t *layer_hint)
{
struct in_addr in;
routerinfo_t *exitrouter;
int reason = *(cell->payload+RELAY_HEADER_SIZE);
+ (void) layer_hint; /* unused */
if (rh->length > 0 && edge_reason_is_retriable(reason) &&
- conn->type == CONN_TYPE_AP) {
+ conn->_base.type == CONN_TYPE_AP) {
log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
safe_str(conn->socks_request->address),
connection_edge_end_reason_str(reason));
@@ -696,19 +717,35 @@ connection_edge_process_end_not_open(
/* rewrite it to an IP if we learned one. */
addressmap_rewrite(conn->socks_request->address,
sizeof(conn->socks_request->address));
+ if (conn->_base.chosen_exit_optional) {
+ /* stop wanting a specific exit */
+ conn->_base.chosen_exit_optional = 0;
+ tor_free(conn->chosen_exit_name);
+ }
if (connection_ap_detach_retriable(conn, circ) >= 0)
return 0;
/* else, conn will get closed below */
break;
+ case END_STREAM_REASON_CONNECTREFUSED:
+ if (!conn->_base.chosen_exit_optional)
+ break; /* break means it'll close, below */
+ /* Else fall through: expire this circuit, clear the
+ * chosen_exit_name field, and try again. */
case END_STREAM_REASON_RESOLVEFAILED:
+ case END_STREAM_REASON_TIMEOUT:
case END_STREAM_REASON_MISC:
if (client_dns_incr_failures(conn->socks_request->address)
< MAX_RESOLVE_FAILURES) {
/* We haven't retried too many times; reattach the connection. */
circuit_log_path(LOG_INFO,LD_APP,circ);
- tor_assert(circ->timestamp_dirty);
- circ->timestamp_dirty -= get_options()->MaxCircuitDirtiness;
+ tor_assert(circ->_base.timestamp_dirty);
+ circ->_base.timestamp_dirty -= get_options()->MaxCircuitDirtiness;
+ if (conn->_base.chosen_exit_optional) {
+ /* stop wanting a specific exit */
+ conn->_base.chosen_exit_optional = 0;
+ tor_free(conn->chosen_exit_name);
+ }
if (connection_ap_detach_retriable(conn, circ) >= 0)
return 0;
/* else, conn will get closed below */
@@ -729,6 +766,11 @@ connection_edge_process_end_not_open(
exitrouter->exit_policy =
router_parse_addr_policy_from_string("reject *:*", -1);
}
+ if (conn->_base.chosen_exit_optional) {
+ /* stop wanting a specific exit */
+ conn->_base.chosen_exit_optional = 0;
+ tor_free(conn->chosen_exit_name);
+ }
if (connection_ap_detach_retriable(conn, circ) >= 0)
return 0;
/* else, will close below */
@@ -740,12 +782,13 @@ connection_edge_process_end_not_open(
log_info(LD_APP,
"Edge got end (%s) before we're connected. Marking for close.",
connection_edge_end_reason_str(rh->length > 0 ? reason : -1));
- if (conn->type == CONN_TYPE_AP) {
+ if (conn->_base.type == CONN_TYPE_AP) {
circuit_log_path(LOG_INFO,LD_APP,circ);
connection_mark_unattached_ap(conn, reason);
} else {
- conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */
- connection_mark_for_close(conn);
+ /* we just got an 'end', don't need to send one */
+ conn->_base.edge_has_sent_end = 1;
+ connection_mark_for_close(TO_CONN(conn));
}
return 0;
}
@@ -760,22 +803,29 @@ connection_edge_process_end_not_open(
static int
connection_edge_process_relay_cell_not_open(
relay_header_t *rh, cell_t *cell, circuit_t *circ,
- connection_t *conn, crypt_path_t *layer_hint)
+ edge_connection_t *conn, crypt_path_t *layer_hint)
{
- if (rh->command == RELAY_COMMAND_END)
- return connection_edge_process_end_not_open(rh, cell, circ, conn,
- layer_hint);
+ if (rh->command == RELAY_COMMAND_END) {
+ if (CIRCUIT_IS_ORIGIN(circ))
+ return connection_edge_process_end_not_open(rh, cell,
+ TO_ORIGIN_CIRCUIT(circ), conn,
+ layer_hint);
+ else
+ return 0;
+ }
- if (conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_CONNECTED) {
- if (conn->state != AP_CONN_STATE_CONNECT_WAIT) {
+ if (conn->_base.type == CONN_TYPE_AP &&
+ rh->command == RELAY_COMMAND_CONNECTED) {
+ tor_assert(CIRCUIT_IS_ORIGIN(circ));
+ if (conn->_base.state != AP_CONN_STATE_CONNECT_WAIT) {
log_warn(LD_APP,"Got 'connected' while not in state connect_wait. "
"Dropping.");
return 0;
}
// log_fn(LOG_INFO,"Connected! Notifying application.");
- conn->state = AP_CONN_STATE_OPEN;
+ conn->_base.state = AP_CONN_STATE_OPEN;
log_info(LD_APP,"'connected' received after %d seconds.",
- (int)(time(NULL) - conn->timestamp_lastread));
+ (int)(time(NULL) - conn->_base.timestamp_lastread));
if (rh->length >= 4) {
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
int ttl;
@@ -794,25 +844,29 @@ connection_edge_process_relay_cell_not_open(
client_dns_set_addressmap(conn->socks_request->address, addr,
conn->chosen_exit_name, ttl);
}
- circuit_log_path(LOG_INFO,LD_APP,circ);
- connection_ap_handshake_socks_reply(conn, NULL, 0, SOCKS5_SUCCEEDED);
+ circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
+ /* don't send a socks reply to transparent conns */
+ if (!conn->socks_request->has_finished)
+ connection_ap_handshake_socks_reply(conn, NULL, 0, SOCKS5_SUCCEEDED);
/* handle anything that might have queued */
if (connection_edge_package_raw_inbuf(conn, 1) < 0) {
/* (We already sent an end cell if possible) */
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return 0;
}
return 0;
}
- if (conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_RESOLVED) {
+ if (conn->_base.type == CONN_TYPE_AP &&
+ rh->command == RELAY_COMMAND_RESOLVED) {
int ttl;
int answer_len;
- if (conn->state != AP_CONN_STATE_RESOLVE_WAIT) {
+ if (conn->_base.state != AP_CONN_STATE_RESOLVE_WAIT) {
log_warn(LD_APP,"Got a 'resolved' cell while not in state resolve_wait. "
"Dropping.");
return 0;
}
- tor_assert(conn->socks_request->command == SOCKS_COMMAND_RESOLVE);
+ tor_assert(conn->socks_request->command == SOCKS_COMMAND_RESOLVE ||
+ conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR);
answer_len = cell->payload[RELAY_HEADER_SIZE+1];
if (rh->length < 2 || answer_len+2>rh->length) {
log_warn(LD_PROTOCOL, "Dropping malformed 'resolved' cell");
@@ -836,8 +890,8 @@ connection_edge_process_relay_cell_not_open(
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Got an unexpected relay command %d, in state %d (%s). Dropping.",
- rh->command, conn->state,
- conn_state_to_string(conn->type, conn->state));
+ rh->command, conn->_base.state,
+ conn_state_to_string(conn->_base.type, conn->_base.state));
return 0; /* for forward compatibility, don't kill the circuit */
// connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL,
// conn->cpath_layer);
@@ -856,7 +910,7 @@ connection_edge_process_relay_cell_not_open(
*/
static int
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
- connection_t *conn,
+ edge_connection_t *conn,
crypt_path_t *layer_hint)
{
static int num_seen=0;
@@ -881,13 +935,13 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
/* either conn is NULL, in which case we've got a control cell, or else
* conn points to the recognized stream. */
- if (conn && !connection_state_is_open(conn))
+ if (conn && !connection_state_is_open(TO_CONN(conn)))
return connection_edge_process_relay_cell_not_open(
&rh, cell, circ, conn, layer_hint);
switch (rh.command) {
case RELAY_COMMAND_DROP:
- log_info(domain,"Got a relay-level padding cell. Dropping.");
+// log_info(domain,"Got a relay-level padding cell. Dropping.");
return 0;
case RELAY_COMMAND_BEGIN:
if (layer_hint &&
@@ -909,7 +963,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
"(relay data) circ deliver_window below 0. Killing.");
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL,
conn->cpath_layer);
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return -END_CIRC_REASON_TORPROTOCOL;
}
log_debug(domain,"circ deliver_window now %d.", layer_hint ?
@@ -930,7 +984,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
stats_n_data_bytes_received += rh.length;
connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE,
- rh.length, conn);
+ rh.length, TO_CONN(conn));
connection_edge_consider_sending_sendme(conn);
return 0;
case RELAY_COMMAND_END:
@@ -942,32 +996,21 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
}
/* XXX add to this log_fn the exit node's nickname? */
log_info(domain,"%d: end cell (%s) for stream %d. Removing stream.",
- conn->s,
+ conn->_base.s,
connection_edge_end_reason_str(rh.length > 0 ?
*(char *)(cell->payload+RELAY_HEADER_SIZE) : -1),
conn->stream_id);
if (conn->socks_request && !conn->socks_request->has_finished)
log_warn(LD_BUG,
"Bug: open stream hasn't sent socks answer yet? Closing.");
-#ifdef HALF_OPEN
- conn->done_sending = 1;
- shutdown(conn->s, 1); /* XXX check return; refactor NM */
- if (conn->done_receiving) {
- /* We just *got* an end; no reason to send one. */
- conn->has_sent_end = 1;
- connection_mark_for_close(conn);
- conn->hold_open_until_flushed = 1;
- }
-#else
/* We just *got* an end; no reason to send one. */
- conn->has_sent_end = 1;
- if (!conn->marked_for_close) {
+ conn->_base.edge_has_sent_end = 1;
+ if (!conn->_base.marked_for_close) {
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
- connection_mark_for_close(conn);
- conn->hold_open_until_flushed = 1;
+ connection_mark_for_close(TO_CONN(conn));
+ conn->_base.hold_open_until_flushed = 1;
}
-#endif
return 0;
case RELAY_COMMAND_EXTEND:
if (conn) {
@@ -982,12 +1025,13 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
return 0;
}
log_debug(domain,"Got an extended cell! Yay.");
- if ((reason = circuit_finish_handshake(circ, CELL_CREATED,
+ if ((reason = circuit_finish_handshake(TO_ORIGIN_CIRCUIT(circ),
+ CELL_CREATED,
cell->payload+RELAY_HEADER_SIZE)) < 0) {
log_warn(domain,"circuit_finish_handshake failed.");
return reason;
}
- if ((reason=circuit_send_next_onion_skin(circ))<0) {
+ if ((reason=circuit_send_next_onion_skin(TO_ORIGIN_CIRCUIT(circ)))<0) {
log_info(domain,"circuit_send_next_onion_skin() failed.");
return reason;
}
@@ -1000,7 +1044,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
if (circ->n_conn) {
uint8_t reason = *(uint8_t*)(cell->payload + RELAY_HEADER_SIZE);
connection_or_send_destroy(circ->n_circ_id, circ->n_conn, reason);
- circuit_set_circid_orconn(circ, 0, NULL, N_CONN_CHANGED);
+ circuit_set_n_circid_orconn(circ, 0, NULL);
}
log_debug(LD_EXIT, "Processed 'truncate', replying.");
{
@@ -1015,7 +1059,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
log_warn(LD_EXIT,"'truncated' unsupported at non-origin. Dropping.");
return 0;
}
- circuit_truncated(circ, layer_hint);
+ circuit_truncated(TO_ORIGIN_CIRCUIT(circ), layer_hint);
return 0;
case RELAY_COMMAND_CONNECTED:
if (conn) {
@@ -1045,11 +1089,11 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
conn->package_window += STREAMWINDOW_INCREMENT;
log_debug(domain,"stream-level sendme, packagewindow now %d.",
conn->package_window);
- connection_start_reading(conn);
+ connection_start_reading(TO_CONN(conn));
/* handle whatever might still be on the inbuf */
if (connection_edge_package_raw_inbuf(conn, 1) < 0) {
/* (We already sent an end cell if possible) */
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
return 0;
}
return 0;
@@ -1065,7 +1109,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
circ->purpose);
return 0;
}
- connection_exit_begin_resolve(cell, circ);
+ connection_exit_begin_resolve(cell, TO_OR_CIRCUIT(circ));
return 0;
case RELAY_COMMAND_RESOLVED:
if (conn) {
@@ -1108,7 +1152,7 @@ uint64_t stats_n_data_bytes_received = 0;
* be marked for close, else return 0.
*/
int
-connection_edge_package_raw_inbuf(connection_t *conn, int package_partial)
+connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial)
{
size_t amount_to_process, length;
char payload[CELL_PAYLOAD_SIZE];
@@ -1116,11 +1160,11 @@ connection_edge_package_raw_inbuf(connection_t *conn, int package_partial)
unsigned domain = conn->cpath_layer ? LD_APP : LD_EXIT;
tor_assert(conn);
- tor_assert(!connection_speaks_cells(conn));
- if (conn->marked_for_close) {
+
+ if (conn->_base.marked_for_close) {
log_warn(LD_BUG,
"Bug: called on conn that's already marked for close at %s:%d.",
- conn->marked_for_close_file, conn->marked_for_close);
+ conn->_base.marked_for_close_file, conn->_base.marked_for_close);
return 0;
}
@@ -1138,11 +1182,11 @@ repeat_connection_edge_package_raw_inbuf:
if (conn->package_window <= 0) {
log_info(domain,"called with package_window %d. Skipping.",
conn->package_window);
- connection_stop_reading(conn);
+ connection_stop_reading(TO_CONN(conn));
return 0;
}
- amount_to_process = buf_datalen(conn->inbuf);
+ amount_to_process = buf_datalen(conn->_base.inbuf);
if (!amount_to_process)
return 0;
@@ -1158,10 +1202,10 @@ repeat_connection_edge_package_raw_inbuf:
stats_n_data_bytes_packaged += length;
stats_n_data_cells_packaged += 1;
- connection_fetch_from_buf(payload, length, conn);
+ connection_fetch_from_buf(payload, length, TO_CONN(conn));
- log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->s,
- (int)length, (int)buf_datalen(conn->inbuf));
+ log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->_base.s,
+ (int)length, (int)buf_datalen(conn->_base.inbuf));
if (connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA,
payload, length, conn->cpath_layer) < 0)
@@ -1177,7 +1221,7 @@ repeat_connection_edge_package_raw_inbuf:
}
if (--conn->package_window <= 0) { /* is it 0 after decrement? */
- connection_stop_reading(conn);
+ connection_stop_reading(TO_CONN(conn));
log_debug(domain,"conn->package_window reached 0.");
circuit_consider_stop_edge_reading(circ, conn->cpath_layer);
return 0; /* don't process the inbuf any more */
@@ -1195,11 +1239,11 @@ repeat_connection_edge_package_raw_inbuf:
* low, send back a suitable number of stream-level sendme cells.
*/
void
-connection_edge_consider_sending_sendme(connection_t *conn)
+connection_edge_consider_sending_sendme(edge_connection_t *conn)
{
circuit_t *circ;
- if (connection_outbuf_too_full(conn))
+ if (connection_outbuf_too_full(TO_CONN(conn)))
return;
circ = circuit_get_by_edge_conn(conn);
@@ -1213,7 +1257,7 @@ connection_edge_consider_sending_sendme(connection_t *conn)
while (conn->deliver_window < STREAMWINDOW_START - STREAMWINDOW_INCREMENT) {
log_debug(conn->cpath_layer?LD_APP:LD_EXIT,
"Outbuf %d, Queueing stream sendme.",
- (int)conn->outbuf_flushlen);
+ (int)conn->_base.outbuf_flushlen);
conn->deliver_window += STREAMWINDOW_INCREMENT;
if (connection_edge_send_command(conn, circ, RELAY_COMMAND_SENDME,
NULL, 0, conn->cpath_layer) < 0) {
@@ -1234,10 +1278,12 @@ circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
log_debug(layer_hint?LD_APP:LD_EXIT,"resuming");
- /* have to check both n_streams and p_streams, to handle rendezvous */
- if (circuit_resume_edge_reading_helper(circ->n_streams, circ, layer_hint)
- >= 0)
- circuit_resume_edge_reading_helper(circ->p_streams, circ, layer_hint);
+ if (CIRCUIT_IS_ORIGIN(circ))
+ circuit_resume_edge_reading_helper(TO_ORIGIN_CIRCUIT(circ)->p_streams,
+ circ, layer_hint);
+ else
+ circuit_resume_edge_reading_helper(TO_OR_CIRCUIT(circ)->n_streams,
+ circ, layer_hint);
}
/** A helper function for circuit_resume_edge_reading() above.
@@ -1245,21 +1291,21 @@ circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
* of a linked list of edge streams that should each be considered.
*/
static int
-circuit_resume_edge_reading_helper(connection_t *conn,
+circuit_resume_edge_reading_helper(edge_connection_t *conn,
circuit_t *circ,
crypt_path_t *layer_hint)
{
for ( ; conn; conn=conn->next_stream) {
- if (conn->marked_for_close)
+ if (conn->_base.marked_for_close)
continue;
if ((!layer_hint && conn->package_window > 0) ||
(layer_hint && conn->package_window > 0 &&
conn->cpath_layer == layer_hint)) {
- connection_start_reading(conn);
+ connection_start_reading(TO_CONN(conn));
/* handle whatever might still be on the inbuf */
if (connection_edge_package_raw_inbuf(conn, 1)<0) {
/* (We already sent an end cell if possible) */
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
continue;
}
@@ -1282,16 +1328,17 @@ circuit_resume_edge_reading_helper(connection_t *conn,
static int
circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
{
- connection_t *conn = NULL;
+ edge_connection_t *conn = NULL;
unsigned domain = layer_hint ? LD_APP : LD_EXIT;
if (!layer_hint) {
+ or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
log_debug(domain,"considering circ->package_window %d",
circ->package_window);
if (circ->package_window <= 0) {
log_debug(domain,"yes, not-at-origin. stopped.");
- for (conn = circ->n_streams; conn; conn=conn->next_stream)
- connection_stop_reading(conn);
+ for (conn = or_circ->n_streams; conn; conn=conn->next_stream)
+ connection_stop_reading(TO_CONN(conn));
return 1;
}
return 0;
@@ -1301,12 +1348,16 @@ circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
layer_hint->package_window);
if (layer_hint->package_window <= 0) {
log_debug(domain,"yes, at-origin. stopped.");
+#if 0
+ // XXXX NM DEAD CODE.
for (conn = circ->n_streams; conn; conn=conn->next_stream)
if (conn->cpath_layer == layer_hint)
connection_stop_reading(conn);
- for (conn = circ->p_streams; conn; conn=conn->next_stream)
+#endif
+ for (conn = TO_ORIGIN_CIRCUIT(circ)->p_streams; conn;
+ conn=conn->next_stream)
if (conn->cpath_layer == layer_hint)
- connection_stop_reading(conn);
+ connection_stop_reading(TO_CONN(conn));
return 1;
}
return 0;
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index ec6f299165..0b61004d0a 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -14,10 +14,9 @@ const char rendclient_c_id[] =
/** Called when we've established a circuit to an introduction point:
* send the introduction request. */
void
-rend_client_introcirc_has_opened(circuit_t *circ)
+rend_client_introcirc_has_opened(origin_circuit_t *circ)
{
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
+ tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
tor_assert(circ->cpath);
log_info(LD_REND,"introcirc is open");
@@ -28,17 +27,17 @@ rend_client_introcirc_has_opened(circuit_t *circ)
* it fails, mark the circ for close and return -1. else return 0.
*/
static int
-rend_client_send_establish_rendezvous(circuit_t *circ)
+rend_client_send_establish_rendezvous(origin_circuit_t *circ)
{
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+ tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell");
if (crypto_rand(circ->rend_cookie, REND_COOKIE_LEN) < 0) {
log_warn(LD_BUG, "Internal error: Couldn't produce random cookie.");
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
- if (connection_edge_send_command(NULL,circ,
+ if (connection_edge_send_command(NULL,TO_CIRCUIT(circ),
RELAY_COMMAND_ESTABLISH_RENDEZVOUS,
circ->rend_cookie, REND_COOKIE_LEN,
circ->cpath->prev)<0) {
@@ -54,7 +53,8 @@ rend_client_send_establish_rendezvous(circuit_t *circ)
* down introcirc if possible.
*/
int
-rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
+rend_client_send_introduction(origin_circuit_t *introcirc,
+ origin_circuit_t *rendcirc)
{
size_t payload_len;
int r;
@@ -64,8 +64,8 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
crypt_path_t *cpath;
off_t dh_offset;
- tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
- tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY);
+ tor_assert(introcirc->_base.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+ tor_assert(rendcirc->_base.purpose == CIRCUIT_PURPOSE_C_REND_READY);
tor_assert(!rend_cmp_service_ids(introcirc->rend_query,
rendcirc->rend_query));
@@ -111,13 +111,15 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
sizeof(tmp)-(7+DIGEST_LEN+2));
set_uint16(tmp+7+DIGEST_LEN, htons(klen));
- memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie, REND_COOKIE_LEN);
+ memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie,
+ REND_COOKIE_LEN);
dh_offset = 7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
} else {
/* Version 0. */
strncpy(tmp, rendcirc->build_state->chosen_exit->nickname,
(MAX_NICKNAME_LEN+1)); /* nul pads */
- memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
+ memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie,
+ REND_COOKIE_LEN);
dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
}
@@ -141,7 +143,7 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
tor_assert(DIGEST_LEN + r <= RELAY_PAYLOAD_SIZE); /* we overran something */
payload_len = DIGEST_LEN + r;
- if (connection_edge_send_command(NULL, introcirc,
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(introcirc),
RELAY_COMMAND_INTRODUCE1,
payload, payload_len,
introcirc->cpath->prev)<0) {
@@ -151,22 +153,21 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
}
/* Now, we wait for an ACK or NAK on this circuit. */
- introcirc->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT;
+ introcirc->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT;
return 0;
err:
- circuit_mark_for_close(introcirc, END_CIRC_AT_ORIGIN);
- circuit_mark_for_close(rendcirc, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_AT_ORIGIN);
return -1;
}
/** Called when a rendezvous circuit is open; sends a establish
* rendezvous circuit as appropriate. */
void
-rend_client_rendcirc_has_opened(circuit_t *circ)
+rend_client_rendcirc_has_opened(origin_circuit_t *circ)
{
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
+ tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
log_info(LD_REND,"rendcirc is open");
@@ -179,21 +180,21 @@ rend_client_rendcirc_has_opened(circuit_t *circ)
/** Called when get an ACK or a NAK for a REND_INTRODUCE1 cell.
*/
int
-rend_client_introduction_acked(circuit_t *circ,
+rend_client_introduction_acked(origin_circuit_t *circ,
const char *request, size_t request_len)
{
- circuit_t *rendcirc;
+ origin_circuit_t *rendcirc;
+ (void) request; // XXXX Use this.
- if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
+ if (circ->_base.purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
log_warn(LD_PROTOCOL,
"Received REND_INTRODUCE_ACK on unexpected circuit %d.",
- circ->n_circ_id);
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circ->_base.n_circ_id);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
tor_assert(circ->build_state->chosen_exit);
- tor_assert(circ->build_state->chosen_exit->nickname);
if (request_len == 0) {
/* It's an ACK; the introduction point relayed our introduction request. */
@@ -204,16 +205,16 @@ rend_client_introduction_acked(circuit_t *circ,
rendcirc = circuit_get_by_rend_query_and_purpose(
circ->rend_query, CIRCUIT_PURPOSE_C_REND_READY);
if (rendcirc) { /* remember the ack */
- rendcirc->purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
+ rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
} else {
log_info(LD_REND,"...Found no rend circ. Dropping on the floor.");
}
/* close the circuit: we won't need it anymore. */
- circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circ->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACKED;
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
} else {
/* It's a NAK; the introduction point didn't relay our request. */
- circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
+ circ->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
/* Remove this intro point from the set of viable introduction
* points. If any remain, extend to a new one and try again.
* If none remain, refetch the service descriptor.
@@ -228,14 +229,14 @@ rend_client_introduction_acked(circuit_t *circ,
if (!extend_info) {
log_warn(LD_REND, "No introduction points left for %s. Closing.",
escaped_safe_str(circ->rend_query));
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
log_info(LD_REND,
"Got nack for %s from %s. Re-extending circ %d, "
"this time to %s.",
escaped_safe_str(circ->rend_query),
- circ->build_state->chosen_exit->nickname, circ->n_circ_id,
+ circ->build_state->chosen_exit->nickname, circ->_base.n_circ_id,
extend_info->nickname);
result = circuit_extend_to_new_exit(circ, extend_info);
extend_info_free(extend_info);
@@ -339,36 +340,38 @@ rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
* the circuit to C_REND_READY.
*/
int
-rend_client_rendezvous_acked(circuit_t *circ, const char *request,
+rend_client_rendezvous_acked(origin_circuit_t *circ, const char *request,
size_t request_len)
{
+ (void) request;
+ (void) request_len;
/* we just got an ack for our establish-rendezvous. switch purposes. */
- if (circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
+ if (circ->_base.purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
log_warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
"Closing circ.");
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
log_info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
"rendezvous.");
- circ->purpose = CIRCUIT_PURPOSE_C_REND_READY;
+ circ->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY;
return 0;
}
/** Bob sent us a rendezvous cell; join the circuits. */
int
-rend_client_receive_rendezvous(circuit_t *circ, const char *request,
+rend_client_receive_rendezvous(origin_circuit_t *circ, const char *request,
size_t request_len)
{
crypt_path_t *hop;
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
- if ((circ->purpose != CIRCUIT_PURPOSE_C_REND_READY &&
- circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
+ if ((circ->_base.purpose != CIRCUIT_PURPOSE_C_REND_READY &&
+ circ->_base.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
|| !circ->build_state->pending_final_cpath) {
log_warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
"expecting it. Closing.");
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
@@ -402,7 +405,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request,
hop->dh_handshake_state = NULL;
/* All is well. Extend the circuit. */
- circ->purpose = CIRCUIT_PURPOSE_C_REND_JOINED;
+ circ->_base.purpose = CIRCUIT_PURPOSE_C_REND_JOINED;
hop->state = CPATH_STATE_OPEN;
/* set the windows to default. these are the windows
* that alice thinks bob has.
@@ -414,7 +417,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request,
circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
return 0;
err:
- circuit_mark_for_close(circ, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_AT_ORIGIN);
return -1;
}
@@ -426,7 +429,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request,
void
rend_client_desc_here(const char *query)
{
- connection_t *conn;
+ edge_connection_t *conn;
rend_cache_entry_t *entry;
time_t now = time(NULL);
int i, n_conns;
@@ -435,25 +438,26 @@ rend_client_desc_here(const char *query)
get_connection_array(&carray, &n_conns);
for (i = 0; i < n_conns; ++i) {
- conn = carray[i];
- if (conn->type != CONN_TYPE_AP ||
- conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
- conn->marked_for_close ||
- rend_cmp_service_ids(query, conn->rend_query))
+ if (carray[i]->type != CONN_TYPE_AP ||
+ carray[i]->state != AP_CONN_STATE_RENDDESC_WAIT ||
+ carray[i]->marked_for_close)
+ continue;
+ conn = TO_EDGE_CONN(carray[i]);
+ if (rend_cmp_service_ids(query, conn->rend_query))
continue;
- assert_connection_ok(conn, now);
+ assert_connection_ok(TO_CONN(conn), now);
if (rend_cache_lookup_entry(conn->rend_query, -1, &entry) == 1 &&
entry->parsed->n_intro_points > 0) {
/* either this fetch worked, or it failed but there was a
* valid entry from before which we should reuse */
log_info(LD_REND,"Rend desc is usable. Launching circuits.");
- conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
/* restart their timeout values, so they get a fair shake at
* connecting to the hidden service. */
- conn->timestamp_created = now;
- conn->timestamp_lastread = now;
- conn->timestamp_lastwritten = now;
+ conn->_base.timestamp_created = now;
+ conn->_base.timestamp_lastread = now;
+ conn->_base.timestamp_lastwritten = now;
if (connection_ap_handshake_attach_circuit(conn) < 0) {
/* it will never work */
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 4c60bac803..ab38e9f3a6 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -234,7 +234,7 @@ rend_get_service_id(crypto_pk_env_t *pk, char *out)
/** How old do we let hidden service descriptors get discarding them as too
* old? */
#define REND_CACHE_MAX_AGE (2*24*60*60)
-/** How wrong to we assume our clock may be when checking whether hidden
+/** How wrong do we assume our clock may be when checking whether hidden
* services are too old or too new? */
#define REND_CACHE_MAX_SKEW (24*60*60)
@@ -432,34 +432,41 @@ void
rend_process_relay_cell(circuit_t *circ, int command, size_t length,
const char *payload)
{
+ or_circuit_t *or_circ = NULL;
+ origin_circuit_t *origin_circ = NULL;
int r;
+ if (CIRCUIT_IS_ORIGIN(circ))
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ else
+ or_circ = TO_OR_CIRCUIT(circ);
+
switch (command) {
case RELAY_COMMAND_ESTABLISH_INTRO:
- r = rend_mid_establish_intro(circ,payload,length);
+ r = rend_mid_establish_intro(or_circ,payload,length);
break;
case RELAY_COMMAND_ESTABLISH_RENDEZVOUS:
- r = rend_mid_establish_rendezvous(circ,payload,length);
+ r = rend_mid_establish_rendezvous(or_circ,payload,length);
break;
case RELAY_COMMAND_INTRODUCE1:
- r = rend_mid_introduce(circ,payload,length);
+ r = rend_mid_introduce(or_circ,payload,length);
break;
case RELAY_COMMAND_INTRODUCE2:
- r = rend_service_introduce(circ,payload,length);
+ r = rend_service_introduce(origin_circ,payload,length);
break;
case RELAY_COMMAND_INTRODUCE_ACK:
- r = rend_client_introduction_acked(circ,payload,length);
+ r = rend_client_introduction_acked(origin_circ,payload,length);
break;
case RELAY_COMMAND_RENDEZVOUS1:
- r = rend_mid_rendezvous(circ,payload,length);
+ r = rend_mid_rendezvous(or_circ,payload,length);
break;
case RELAY_COMMAND_RENDEZVOUS2:
- r = rend_client_receive_rendezvous(circ,payload,length);
+ r = rend_client_receive_rendezvous(origin_circ,payload,length);
break;
case RELAY_COMMAND_INTRO_ESTABLISHED:
- r = rend_service_intro_established(circ,payload,length);
+ r = rend_service_intro_established(origin_circ,payload,length);
break;
case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
- r = rend_client_rendezvous_acked(circ,payload,length);
+ r = rend_client_rendezvous_acked(origin_circ,payload,length);
break;
default:
tor_assert(0);
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index c1252289bc..e96d608d59 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -15,7 +15,7 @@ const char rendmid_c_id[] =
* setting the circuit's purpose and service pk digest.
*/
int
-rend_mid_establish_intro(circuit_t *circ, const char *request,
+rend_mid_establish_intro(or_circuit_t *circ, const char *request,
size_t request_len)
{
crypto_pk_env_t *pk = NULL;
@@ -23,7 +23,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
char expected_digest[DIGEST_LEN];
char pk_digest[DIGEST_LEN];
size_t asn1len;
- circuit_t *c;
+ or_circuit_t *c;
char serviceid[REND_SERVICE_ID_LEN+1];
int reason = END_CIRC_REASON_INTERNAL;
@@ -31,7 +31,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
"Received an ESTABLISH_INTRO request on circuit %d",
circ->p_circ_id);
- if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
+ if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit.");
reason = END_CIRC_REASON_TORPROTOCOL;
@@ -87,15 +87,15 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
/* Close any other intro circuits with the same pk. */
c = NULL;
- while ((c = circuit_get_next_by_pk_and_purpose(
- c,pk_digest,CIRCUIT_PURPOSE_INTRO_POINT))) {
- log_info(LD_REND, "Replacing old circuit %d for service %s",
- c->p_circ_id, safe_str(serviceid));
- circuit_mark_for_close(c, END_CIRC_REASON_REQUESTED);
+ while ((c = circuit_get_intro_point(pk_digest))) {
+ log_info(LD_REND, "Replacing old circuit for service %s",
+ safe_str(serviceid));
+ circuit_mark_for_close(TO_CIRCUIT(c), END_CIRC_REASON_REQUESTED);
+ /* Now it's marked, and it won't be returned next time. */
}
/* Acknowledge the request. */
- if (connection_edge_send_command(NULL,circ,
+ if (connection_edge_send_command(NULL,TO_CIRCUIT(circ),
RELAY_COMMAND_INTRO_ESTABLISHED,
"", 0, NULL)<0) {
log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell.");
@@ -103,8 +103,8 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
}
/* Now, set up this circuit. */
- circ->purpose = CIRCUIT_PURPOSE_INTRO_POINT;
- memcpy(circ->rend_pk_digest, pk_digest, DIGEST_LEN);
+ circ->_base.purpose = CIRCUIT_PURPOSE_INTRO_POINT;
+ memcpy(circ->rend_token, pk_digest, DIGEST_LEN);
log_info(LD_REND,
"Established introduction point on circuit %d for service %s",
@@ -116,7 +116,7 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
reason = END_CIRC_REASON_TORPROTOCOL;
err:
if (pk) crypto_free_pk_env(pk);
- circuit_mark_for_close(circ, reason);
+ circuit_mark_for_close(TO_CIRCUIT(circ), reason);
return -1;
}
@@ -125,20 +125,23 @@ rend_mid_establish_intro(circuit_t *circ, const char *request,
* INTRODUCE2 cell.
*/
int
-rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
+rend_mid_introduce(or_circuit_t *circ, const char *request, size_t request_len)
{
- circuit_t *intro_circ;
+ or_circuit_t *intro_circ;
char serviceid[REND_SERVICE_ID_LEN+1];
char nak_body[1];
- if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
+ if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) {
log_warn(LD_PROTOCOL,
"Rejecting INTRODUCE1 on non-OR or non-edge circuit %d.",
circ->p_circ_id);
goto err;
}
- /* change to MAX_HEX_NICKNAME_LEN once 0.0.9.x is obsolete */
+ /* We could change this to MAX_HEX_NICKNAME_LEN now that 0.0.9.x is
+ * obsolete; however, there isn't much reason to do so, and we're going
+ * to revise this protocol anyway.
+ */
if (request_len < (DIGEST_LEN+(MAX_NICKNAME_LEN+1)+REND_COOKIE_LEN+
DH_KEY_LEN+CIPHER_KEY_LEN+PKCS1_OAEP_PADDING_OVERHEAD)) {
log_warn(LD_PROTOCOL, "Impossibly short INTRODUCE1 cell on circuit %d; "
@@ -150,8 +153,7 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
base32_encode(serviceid, REND_SERVICE_ID_LEN+1, request,10);
/* The first 20 bytes are all we look at: they have a hash of Bob's PK. */
- intro_circ = circuit_get_next_by_pk_and_purpose(
- NULL, request, CIRCUIT_PURPOSE_INTRO_POINT);
+ intro_circ = circuit_get_intro_point(request);
if (!intro_circ) {
log_info(LD_REND,
"No intro circ found for INTRODUCE1 cell (%s) from circuit %d; "
@@ -163,10 +165,11 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
log_info(LD_REND,
"Sending introduction request for service %s "
"from circ %d to circ %d",
- safe_str(serviceid), circ->p_circ_id, intro_circ->p_circ_id);
+ safe_str(serviceid), circ->p_circ_id,
+ intro_circ->p_circ_id);
/* Great. Now we just relay the cell down the circuit. */
- if (connection_edge_send_command(NULL, intro_circ,
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(intro_circ),
RELAY_COMMAND_INTRODUCE2,
request, request_len, NULL)) {
log_warn(LD_GENERAL,
@@ -174,10 +177,11 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
goto err;
}
/* And sent an ack down Alice's circuit. Empty body means succeeded. */
- if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK,
+ if (connection_edge_send_command(NULL,TO_CIRCUIT(circ),
+ RELAY_COMMAND_INTRODUCE_ACK,
NULL,0,NULL)) {
log_warn(LD_GENERAL, "Unable to send INTRODUCE_ACK cell to Tor client.");
- circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return -1;
}
@@ -185,11 +189,12 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
err:
/* Send the client an NACK */
nak_body[0] = 1;
- if (connection_edge_send_command(NULL,circ,RELAY_COMMAND_INTRODUCE_ACK,
+ if (connection_edge_send_command(NULL,TO_CIRCUIT(circ),
+ RELAY_COMMAND_INTRODUCE_ACK,
nak_body, 1, NULL)) {
log_warn(LD_GENERAL, "Unable to send NAK to Tor client.");
/* Is this right? */
- circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
}
return -1;
}
@@ -198,13 +203,13 @@ rend_mid_introduce(circuit_t *circ, const char *request, size_t request_len)
* rendezvous cookie.
*/
int
-rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
+rend_mid_establish_rendezvous(or_circuit_t *circ, const char *request,
size_t request_len)
{
char hexid[9];
int reason = END_CIRC_REASON_TORPROTOCOL;
- if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
+ if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) {
log_warn(LD_PROTOCOL,
"Tried to establish rendezvous on non-OR or non-edge circuit.");
goto err;
@@ -222,7 +227,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
}
/* Acknowledge the request. */
- if (connection_edge_send_command(NULL,circ,
+ if (connection_edge_send_command(NULL,TO_CIRCUIT(circ),
RELAY_COMMAND_RENDEZVOUS_ESTABLISHED,
"", 0, NULL)<0) {
log_warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell.");
@@ -230,8 +235,8 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
goto err;
}
- circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
- memcpy(circ->rend_cookie, request, REND_COOKIE_LEN);
+ circ->_base.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
+ memcpy(circ->rend_token, request, REND_COOKIE_LEN);
base16_encode(hexid,9,request,4);
@@ -241,7 +246,7 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
return 0;
err:
- circuit_mark_for_close(circ, reason);
+ circuit_mark_for_close(TO_CIRCUIT(circ), reason);
return -1;
}
@@ -250,9 +255,10 @@ rend_mid_establish_rendezvous(circuit_t *circ, const char *request,
* connecting the two circuits.
*/
int
-rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
+rend_mid_rendezvous(or_circuit_t *circ, const char *request,
+ size_t request_len)
{
- circuit_t *rend_circ;
+ or_circuit_t *rend_circ;
char hexid[9];
int reason = END_CIRC_REASON_INTERNAL;
base16_encode(hexid,9,request,request_len<4?request_len:4);
@@ -263,7 +269,7 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
circ->p_circ_id, hexid);
}
- if (circ->purpose != CIRCUIT_PURPOSE_OR || circ->n_conn) {
+ if (circ->_base.purpose != CIRCUIT_PURPOSE_OR || circ->_base.n_conn) {
log_info(LD_REND,
"Tried to complete rendezvous on non-OR or non-edge circuit %d.",
circ->p_circ_id);
@@ -289,12 +295,12 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
}
/* Send the RENDEZVOUS2 cell to Alice. */
- if (connection_edge_send_command(NULL, rend_circ,
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(rend_circ),
RELAY_COMMAND_RENDEZVOUS2,
request+REND_COOKIE_LEN,
request_len-REND_COOKIE_LEN, NULL)) {
log_warn(LD_GENERAL,
- "Unable to send RENDEZVOUS2 cell to OP on circuit %d.",
+ "Unable to send RENDEZVOUS2 cell to client on circuit %d.",
rend_circ->p_circ_id);
goto err;
}
@@ -304,16 +310,16 @@ rend_mid_rendezvous(circuit_t *circ, const char *request, size_t request_len)
"Completing rendezvous: circuit %d joins circuit %d (cookie %s)",
circ->p_circ_id, rend_circ->p_circ_id, hexid);
- circ->purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED;
- rend_circ->purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED;
- memset(circ->rend_cookie, 0, REND_COOKIE_LEN);
+ circ->_base.purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED;
+ rend_circ->_base.purpose = CIRCUIT_PURPOSE_REND_ESTABLISHED;
+ memset(circ->rend_token, 0, REND_COOKIE_LEN);
rend_circ->rend_splice = circ;
circ->rend_splice = rend_circ;
return 0;
err:
- circuit_mark_for_close(circ, reason);
+ circuit_mark_for_close(TO_CIRCUIT(circ), reason);
return -1;
}
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 45454985ed..0cda1d95c0 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -11,8 +11,8 @@ const char rendservice_c_id[] =
#include "or.h"
-static circuit_t *find_intro_circuit(routerinfo_t *router,
- const char *pk_digest);
+static origin_circuit_t *find_intro_circuit(routerinfo_t *router,
+ const char *pk_digest);
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
@@ -179,7 +179,7 @@ parse_port_config(const char *string)
} else {
addrport = smartlist_get(sl,1);
if (strchr(addrport, ':') || strchr(addrport, '.')) {
- if (parse_addr_port(addrport, NULL, &addr, &p)<0) {
+ if (parse_addr_port(LOG_WARN, addrport, NULL, &addr, &p)<0) {
log_warn(LD_CONFIG,"Unparseable address in hidden service port "
"configuration.");
goto err;
@@ -285,7 +285,7 @@ static void
rend_service_update_descriptor(rend_service_t *service)
{
rend_service_descriptor_t *d;
- circuit_t *circ;
+ origin_circuit_t *circ;
int i,n;
routerinfo_t *router;
@@ -310,7 +310,7 @@ rend_service_update_descriptor(rend_service_t *service)
continue;
}
circ = find_intro_circuit(router, service->pk_digest);
- if (circ && circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
+ if (circ && circ->_base.purpose == CIRCUIT_PURPOSE_S_INTRO) {
/* We have an entirely established intro circuit. */
d->intro_points[d->n_intro_points] = tor_strdup(router->nickname);
d->intro_point_extend_info[d->n_intro_points] =
@@ -410,7 +410,7 @@ rend_service_requires_uptime(rend_service_t *service)
* rendezvous point.
*/
int
-rend_service_introduce(circuit_t *circuit, const char *request,
+rend_service_introduce(origin_circuit_t *circuit, const char *request,
size_t request_len)
{
char *ptr, *r_cookie;
@@ -421,7 +421,7 @@ rend_service_introduce(circuit_t *circuit, const char *request,
int r, i;
size_t len, keylen;
crypto_dh_env_t *dh = NULL;
- circuit_t *launched = NULL;
+ origin_circuit_t *launched = NULL;
crypt_path_t *cpath = NULL;
char serviceid[REND_SERVICE_ID_LEN+1];
char hexcookie[9];
@@ -430,12 +430,12 @@ rend_service_introduce(circuit_t *circuit, const char *request,
base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
circuit->rend_pk_digest,10);
log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %d.",
- escaped(serviceid), circuit->n_circ_id);
+ escaped(serviceid), circuit->_base.n_circ_id);
- if (circuit->purpose != CIRCUIT_PURPOSE_S_INTRO) {
+ if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) {
log_warn(LD_PROTOCOL,
"Got an INTRODUCE2 over a non-introduction circuit %d.",
- circuit->n_circ_id);
+ circuit->_base.n_circ_id);
return -1;
}
@@ -443,7 +443,7 @@ rend_service_introduce(circuit_t *circuit, const char *request,
if (request_len < DIGEST_LEN+REND_COOKIE_LEN+(MAX_NICKNAME_LEN+1)+
DH_KEY_LEN+42) {
log_warn(LD_PROTOCOL, "Got a truncated INTRODUCE2 cell on circ %d.",
- circuit->n_circ_id);
+ circuit->_base.n_circ_id);
return -1;
}
@@ -518,7 +518,7 @@ rend_service_introduce(circuit_t *circuit, const char *request,
ptr=memchr(rp_nickname,0,nickname_field_len);
if (!ptr || ptr == rp_nickname) {
log_warn(LD_PROTOCOL,
- "Couldn't find a null-padded nickname in INTRODUCE2 cell.");
+ "Couldn't find a nul-padded nickname in INTRODUCE2 cell.");
return -1;
}
if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
@@ -609,7 +609,8 @@ rend_service_introduce(circuit_t *circuit, const char *request,
return 0;
err:
if (dh) crypto_dh_free(dh);
- if (launched) circuit_mark_for_close(launched, END_CIRC_AT_ORIGIN);
+ if (launched)
+ circuit_mark_for_close(TO_CIRCUIT(launched), END_CIRC_AT_ORIGIN);
if (extend_info) extend_info_free(extend_info);
return -1;
}
@@ -618,12 +619,12 @@ rend_service_introduce(circuit_t *circuit, const char *request,
* than the last hop: launches a new circuit to the same rendezvous point.
*/
void
-rend_service_relaunch_rendezvous(circuit_t *oldcirc)
+rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
{
- circuit_t *newcirc;
+ origin_circuit_t *newcirc;
cpath_build_state_t *newstate, *oldstate;
- tor_assert(oldcirc->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
+ tor_assert(oldcirc->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
if (!oldcirc->build_state ||
oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
@@ -663,8 +664,10 @@ rend_service_relaunch_rendezvous(circuit_t *oldcirc)
oldstate->pending_final_cpath = NULL;
memcpy(newcirc->rend_query, oldcirc->rend_query, REND_SERVICE_ID_LEN+1);
- memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest, DIGEST_LEN);
- memcpy(newcirc->rend_cookie, oldcirc->rend_cookie, REND_COOKIE_LEN);
+ memcpy(newcirc->rend_pk_digest, oldcirc->rend_pk_digest,
+ DIGEST_LEN);
+ memcpy(newcirc->rend_cookie, oldcirc->rend_cookie,
+ REND_COOKIE_LEN);
}
/** Launch a circuit to serve as an introduction point for the service
@@ -674,7 +677,7 @@ static int
rend_service_launch_establish_intro(rend_service_t *service,
const char *nickname)
{
- circuit_t *launched;
+ origin_circuit_t *launched;
log_info(LD_REND,
"Launching circuit to introduction point %s for service %s",
@@ -695,7 +698,7 @@ rend_service_launch_establish_intro(rend_service_t *service,
sizeof(launched->rend_query));
memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN);
- if (launched->state == CIRCUIT_STATE_OPEN)
+ if (launched->_base.state == CIRCUIT_STATE_OPEN)
rend_service_intro_has_opened(launched);
return 0;
}
@@ -704,7 +707,7 @@ rend_service_launch_establish_intro(rend_service_t *service,
* sends a RELAY_ESTABLISH_INTRO cell.
*/
void
-rend_service_intro_has_opened(circuit_t *circuit)
+rend_service_intro_has_opened(origin_circuit_t *circuit)
{
rend_service_t *service;
size_t len;
@@ -713,8 +716,7 @@ rend_service_intro_has_opened(circuit_t *circuit)
char auth[DIGEST_LEN + 9];
char serviceid[REND_SERVICE_ID_LEN+1];
- tor_assert(circuit->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
- tor_assert(CIRCUIT_IS_ORIGIN(circuit));
+ tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
tor_assert(circuit->cpath);
base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
@@ -723,13 +725,13 @@ rend_service_intro_has_opened(circuit_t *circuit)
service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
if (!service) {
log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
- serviceid, circuit->n_circ_id);
+ serviceid, circuit->_base.n_circ_id);
goto err;
}
log_info(LD_REND,
"Established circuit %d as introduction point for service %s",
- circuit->n_circ_id, serviceid);
+ circuit->_base.n_circ_id, serviceid);
/* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
len = crypto_pk_asn1_encode(service->private_key, buf+2,
@@ -748,29 +750,32 @@ rend_service_intro_has_opened(circuit_t *circuit)
}
len += r;
- if (connection_edge_send_command(NULL, circuit,RELAY_COMMAND_ESTABLISH_INTRO,
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(circuit),
+ RELAY_COMMAND_ESTABLISH_INTRO,
buf, len, circuit->cpath->prev)<0) {
log_info(LD_GENERAL,
"Couldn't send introduction request for service %s on circuit %d",
- serviceid, circuit->n_circ_id);
+ serviceid, circuit->_base.n_circ_id);
goto err;
}
return;
err:
- circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_AT_ORIGIN);
}
/** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a
* live introduction point, and note that the service descriptor is
* now out-of-date.*/
int
-rend_service_intro_established(circuit_t *circuit, const char *request,
+rend_service_intro_established(origin_circuit_t *circuit, const char *request,
size_t request_len)
{
rend_service_t *service;
+ (void) request;
+ (void) request_len;
- if (circuit->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
+ if (circuit->_base.purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
log_warn(LD_PROTOCOL,
"received INTRO_ESTABLISHED cell on non-intro circuit.");
goto err;
@@ -778,15 +783,15 @@ rend_service_intro_established(circuit_t *circuit, const char *request,
service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
if (!service) {
log_warn(LD_REND, "Unknown service on introduction circuit %d.",
- circuit->n_circ_id);
+ circuit->_base.n_circ_id);
goto err;
}
service->desc_is_dirty = time(NULL);
- circuit->purpose = CIRCUIT_PURPOSE_S_INTRO;
+ circuit->_base.purpose = CIRCUIT_PURPOSE_S_INTRO;
return 0;
err:
- circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_AT_ORIGIN);
return -1;
}
@@ -794,7 +799,7 @@ rend_service_intro_established(circuit_t *circuit, const char *request,
* RELAY_COMMAND_RENDEZVOUS1 cell.
*/
void
-rend_service_rendezvous_has_opened(circuit_t *circuit)
+rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
{
rend_service_t *service;
char buf[RELAY_PAYLOAD_SIZE];
@@ -802,7 +807,7 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
char serviceid[REND_SERVICE_ID_LEN+1];
char hexcookie[9];
- tor_assert(circuit->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
+ tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
tor_assert(circuit->cpath);
tor_assert(circuit->build_state);
hop = circuit->build_state->pending_final_cpath;
@@ -815,7 +820,7 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
log_info(LD_REND,
"Done building circuit %d to rendezvous with "
"cookie %s for service %s",
- circuit->n_circ_id, hexcookie, serviceid);
+ circuit->_base.n_circ_id, hexcookie, serviceid);
service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
if (!service) {
@@ -835,7 +840,8 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
DIGEST_LEN);
/* Send the cell */
- if (connection_edge_send_command(NULL, circuit, RELAY_COMMAND_RENDEZVOUS1,
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(circuit),
+ RELAY_COMMAND_RENDEZVOUS1,
buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN,
circuit->cpath->prev)<0) {
log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell.");
@@ -857,11 +863,11 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
circuit->build_state->pending_final_cpath = NULL; /* prevent double-free */
/* Change the circuit purpose. */
- circuit->purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
+ circuit->_base.purpose = CIRCUIT_PURPOSE_S_REND_JOINED;
return;
err:
- circuit_mark_for_close(circuit, END_CIRC_AT_ORIGIN);
+ circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_AT_ORIGIN);
}
/*
@@ -872,15 +878,14 @@ rend_service_rendezvous_has_opened(circuit_t *circuit)
* <b>router</b> for the service whose public key is <b>pk_digest</b>. Return
* NULL if no such service is found.
*/
-static circuit_t *
+static origin_circuit_t *
find_intro_circuit(routerinfo_t *router, const char *pk_digest)
{
- circuit_t *circ = NULL;
+ origin_circuit_t *circ = NULL;
tor_assert(router);
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
CIRCUIT_PURPOSE_S_INTRO))) {
- tor_assert(circ->cpath);
if (!strcasecmp(circ->build_state->chosen_exit->nickname,
router->nickname)) {
return circ;
@@ -890,7 +895,6 @@ find_intro_circuit(routerinfo_t *router, const char *pk_digest)
circ = NULL;
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
- tor_assert(circ->cpath);
if (!strcasecmp(circ->build_state->chosen_exit->nickname,
router->nickname)) {
return circ;
@@ -1086,7 +1090,7 @@ rend_service_dump_stats(int severity)
routerinfo_t *router;
rend_service_t *service;
char *nickname;
- circuit_t *circ;
+ origin_circuit_t *circ;
for (i=0; i < smartlist_len(rend_service_list); ++i) {
service = smartlist_get(rend_service_list, i);
@@ -1106,7 +1110,7 @@ rend_service_dump_stats(int severity)
continue;
}
log(severity, LD_GENERAL, " Intro point at %s: circuit is %s",nickname,
- circuit_state_to_string(circ->state));
+ circuit_state_to_string(circ->_base.state));
}
}
}
@@ -1117,14 +1121,15 @@ rend_service_dump_stats(int severity)
* or 0 for success.
*/
int
-rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ)
+rend_service_set_connection_addr_port(edge_connection_t *conn,
+ origin_circuit_t *circ)
{
rend_service_t *service;
int i;
rend_service_port_config_t *p;
char serviceid[REND_SERVICE_ID_LEN+1];
- tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+ tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
log_debug(LD_REND,"beginning to hunt for addr/port");
base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
circ->rend_pk_digest,10);
@@ -1132,19 +1137,19 @@ rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ)
if (!service) {
log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
"rendezvous circuit %d; closing.",
- serviceid, circ->n_circ_id);
+ serviceid, circ->_base.n_circ_id);
return -1;
}
for (i = 0; i < smartlist_len(service->ports); ++i) {
p = smartlist_get(service->ports, i);
- if (conn->port == p->virtual_port) {
- conn->addr = p->real_addr;
- conn->port = p->real_port;
+ if (conn->_base.port == p->virtual_port) {
+ conn->_base.addr = p->real_addr;
+ conn->_base.port = p->real_port;
return 0;
}
}
log_info(LD_REND, "No virtual port mapping exists for port %d on service %s",
- conn->port,serviceid);
+ conn->_base.port,serviceid);
return -1;
}
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 8caad25ce4..2dceb143b1 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -59,14 +59,13 @@ typedef struct or_history_t {
/** Map from hex OR identity digest to or_history_t. */
static digestmap_t *history_map = NULL;
-/** Return the or_history_t for the named OR, creating it if necessary.
- */
+/** Return the or_history_t for the named OR, creating it if necessary. */
static or_history_t *
get_or_history(const char* id)
{
or_history_t *hist;
- if (!memcmp(id, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DIGEST_LEN))
+ if (tor_mem_is_zero(id, DIGEST_LEN))
return NULL;
hist = digestmap_get(history_map, id);
@@ -83,7 +82,7 @@ get_or_history(const char* id)
/** Return the link_history_t for the link from the first named OR to
* the second, creating it if necessary. (ORs are identified by
- * identity digest)
+ * identity digest.)
*/
static link_history_t *
get_link_history(const char *from_id, const char *to_id)
@@ -93,7 +92,7 @@ get_link_history(const char *from_id, const char *to_id)
orhist = get_or_history(from_id);
if (!orhist)
return NULL;
- if (!memcmp(to_id, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DIGEST_LEN))
+ if (tor_mem_is_zero(to_id, DIGEST_LEN))
return NULL;
lhist = (link_history_t*) digestmap_get(orhist->link_history_map, to_id);
if (!lhist) {
@@ -105,7 +104,7 @@ get_link_history(const char *from_id, const char *to_id)
return lhist;
}
-/** Helper: free storage held by a single link history entry */
+/** Helper: free storage held by a single link history entry. */
static void
_free_link_history(void *val)
{
@@ -113,7 +112,7 @@ _free_link_history(void *val)
tor_free(val);
}
-/** Helper: free storage held by a single OR history entry */
+/** Helper: free storage held by a single OR history entry. */
static void
free_or_history(void *_hist)
{
@@ -141,8 +140,7 @@ update_or_history(or_history_t *hist, time_t when)
}
}
-/** Initialize the static data structures for tracking history.
- */
+/** Initialize the static data structures for tracking history. */
void
rep_hist_init(void)
{
@@ -236,7 +234,7 @@ rep_hist_note_connection_died(const char* id, time_t when)
/** Remember that we successfully extended from the OR with identity
* digest <b>from_id</b> to the OR with identity digest
- * <b>to_name</b>.
+ * <b>to_name</b>.
*/
void
rep_hist_note_extend_succeeded(const char *from_id, const char *to_id)
@@ -344,7 +342,8 @@ rep_hist_dump_stats(time_t now, int severity)
}
/** Remove history info for routers/links that haven't changed since
- * <b>before</b> */
+ * <b>before</b>.
+ */
void
rep_history_clean(time_t before)
{
@@ -387,11 +386,10 @@ rep_history_clean(time_t before)
#define NUM_SECS_BW_SUM_INTERVAL (15*60)
/** How far in the past do we remember and publish bandwidth use? */
#define NUM_SECS_BW_SUM_IS_VALID (24*60*60)
-/** How many bandwidth usage intervals do we remember? (derived.) */
+/** How many bandwidth usage intervals do we remember? (derived) */
#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
-/**
- * Structure to track bandwidth use, and remember the maxima for a given
+/** Structure to track bandwidth use, and remember the maxima for a given
* time period.
*/
typedef struct bw_array_t {
@@ -423,8 +421,7 @@ typedef struct bw_array_t {
uint64_t totals[NUM_TOTALS];
} bw_array_t;
-/** Shift the current period of b forward by one.
- */
+/** Shift the current period of b forward by one. */
static void
commit_max(bw_array_t *b)
{
@@ -444,8 +441,7 @@ commit_max(bw_array_t *b)
b->total_in_period = 0;
}
-/** Shift the current observation time of 'b' forward by one second.
- */
+/** Shift the current observation time of 'b' forward by one second. */
static INLINE void
advance_obs(bw_array_t *b)
{
@@ -470,8 +466,7 @@ advance_obs(bw_array_t *b)
commit_max(b);
}
-/** Add 'n' bytes to the number of bytes in b for second 'when'.
- */
+/** Add 'n' bytes to the number of bytes in b for second 'when'. */
static INLINE void
add_obs(bw_array_t *b, time_t when, uint64_t n)
{
@@ -488,8 +483,7 @@ add_obs(bw_array_t *b, time_t when, uint64_t n)
b->total_in_period += n;
}
-/** Allocate, initialize, and return a new bw_array.
- */
+/** Allocate, initialize, and return a new bw_array. */
static bw_array_t *
bw_array_new(void)
{
@@ -506,8 +500,7 @@ bw_array_new(void)
static bw_array_t *read_array = NULL;
static bw_array_t *write_array = NULL;
-/** Set up read_array and write_array
- */
+/** Set up read_array and write_array. */
static void
bw_arrays_init(void)
{
@@ -563,8 +556,7 @@ find_largest_max(bw_array_t *b)
return max;
}
-/**
- * Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
+/** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
* seconds. Find one sum for reading and one for writing. They don't have
* to be at the same time).
*
@@ -577,15 +569,12 @@ rep_hist_bandwidth_assess(void)
r = find_largest_max(read_array);
w = find_largest_max(write_array);
if (r>w)
- return (int)(w/(double)NUM_SECS_ROLLING_MEASURE);
+ return (int)(U64_TO_DBL(w)/NUM_SECS_ROLLING_MEASURE);
else
- return (int)(r/(double)NUM_SECS_ROLLING_MEASURE);
-
- return 0;
+ return (int)(U64_TO_DBL(r)/NUM_SECS_ROLLING_MEASURE);
}
-/**
- * Print the bandwidth history of b (either read_array or write_array)
+/** Print the bandwidth history of b (either read_array or write_array)
* into the buffer pointed to by buf. The format is simply comma
* separated numbers, from oldest to newest.
*
@@ -619,8 +608,7 @@ rep_hist_fill_bandwidth_history(char *buf, size_t len, bw_array_t *b)
return cp-buf;
}
-/**
- * Allocate and return lines for representing this server's bandwidth
+/** Allocate and return lines for representing this server's bandwidth
* history in its descriptor.
*/
char *
@@ -689,8 +677,7 @@ rep_hist_update_state(or_state_t *state)
state->dirty = 1;
}
-/** Set bandwidth history from our saved state.
- */
+/** Set bandwidth history from our saved state. */
int
rep_hist_load_state(or_state_t *state, char **err)
{
@@ -751,12 +738,17 @@ rep_hist_load_state(or_state_t *state, char **err)
return 0;
}
+/*********************************************************************/
+
/** A list of port numbers that have been used recently. */
static smartlist_t *predicted_ports_list=NULL;
/** The corresponding most recently used time for each port. */
static smartlist_t *predicted_ports_times=NULL;
-/** DOCDOC */
+/** We just got an application request for a connection with
+ * port <b>port</b>. Remember it for the future, so we can keep
+ * some circuits open that will exit to this port.
+ */
static void
add_predicted_port(uint16_t port, time_t now)
{
@@ -770,7 +762,10 @@ add_predicted_port(uint16_t port, time_t now)
smartlist_add(predicted_ports_times, tmp_time);
}
-/** DOCDOC */
+/** Initialize whatever memory and structs are needed for predicting
+ * which ports will be used. Also seed it with port 80, so we'll build
+ * circuits on start-up.
+ */
static void
predicted_ports_init(void)
{
@@ -779,7 +774,9 @@ predicted_ports_init(void)
add_predicted_port(80, time(NULL)); /* add one to kickstart us */
}
-/** DOCDOC */
+/** Free whatever memory is needed for predicting which ports will
+ * be used.
+ */
static void
predicted_ports_free(void)
{
@@ -921,6 +918,22 @@ any_predicted_circuits(time_t now)
predicted_internal_time + PREDICTED_CIRCS_RELEVANCE_TIME >= now;
}
+/** Return 1 if we have no need for circuits currently, else return 0. */
+int
+rep_hist_circbuilding_dormant(time_t now)
+{
+ if (any_predicted_circuits(now))
+ return 0;
+
+ /* see if we'll still need to build testing circuits */
+ if (server_mode(get_options()) && !check_whether_orport_reachable())
+ return 0;
+ if (!check_whether_dirport_reachable())
+ return 0;
+
+ return 1;
+}
+
/** Free all storage held by the OR/link history caches, by the
* bandwidth history arrays, or by the port history. */
void
diff --git a/src/or/router.c b/src/or/router.c
index dd9bde6ff3..a5f8db09ae 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -232,7 +232,8 @@ init_key_from_file(const char *fname)
}
/** Initialize all OR private keys, and the TLS context, as necessary.
- * On OPs, this only initializes the tls context.
+ * On OPs, this only initializes the tls context. Return 0 on success,
+ * or -1 if Tor should die.
*/
int
init_keys(void)
@@ -260,10 +261,10 @@ init_keys(void)
return -1;
set_identity_key(prkey);
/* Create a TLS context; default the client nickname to "client". */
- if (tor_tls_context_new(get_identity_key(), 1,
+ if (tor_tls_context_new(get_identity_key(),
options->Nickname ? options->Nickname : "client",
MAX_SSL_KEY_LIFETIME) < 0) {
- log_err(LD_GENERAL,"Error creating TLS context for OP.");
+ log_err(LD_GENERAL,"Error creating TLS context for Tor client.");
return -1;
}
return 0;
@@ -302,7 +303,7 @@ init_keys(void)
}
/* 3. Initialize link key and TLS context. */
- if (tor_tls_context_new(get_identity_key(), 1, options->Nickname,
+ if (tor_tls_context_new(get_identity_key(), options->Nickname,
MAX_SSL_KEY_LIFETIME) < 0) {
log_err(LD_GENERAL,"Error initializing TLS context");
return -1;
@@ -310,10 +311,6 @@ init_keys(void)
/* 4. Build our router descriptor. */
/* Must be called after keys are initialized. */
mydesc = router_get_my_descriptor();
- if (!mydesc) {
- log_err(LD_GENERAL,"Error initializing descriptor.");
- return -1;
- }
if (authdir_mode(options)) {
const char *m;
/* We need to add our own fingerprint so it gets recognized. */
@@ -321,6 +318,10 @@ init_keys(void)
log_err(LD_GENERAL,"Error adding own fingerprint to approved set");
return -1;
}
+ if (!mydesc) {
+ log_err(LD_GENERAL,"Error initializing descriptor.");
+ return -1;
+ }
if (dirserv_add_descriptor(mydesc, &m) < 0) {
log_err(LD_GENERAL,"Unable to add own descriptor to directory: %s",
m?m:"<unknown error>");
@@ -328,13 +329,6 @@ init_keys(void)
}
}
-#if 0
- tor_snprintf(keydir,sizeof(keydir),"%s/router.desc", datadir);
- log_info(LD_GENERAL,"Dumping descriptor to \"%s\"...",keydir);
- if (write_str_to_file(keydir, mydesc,0)) {
- return -1;
- }
-#endif
/* 5. Dump fingerprint to 'fingerprint' */
tor_snprintf(keydir,sizeof(keydir),"%s/fingerprint", datadir);
log_info(LD_GENERAL,"Dumping fingerprint to \"%s\"...",keydir);
@@ -359,9 +353,7 @@ init_keys(void)
if (!authdir_mode(options))
return 0;
/* 6. [authdirserver only] load approved-routers file */
- tor_snprintf(keydir,sizeof(keydir),"%s/approved-routers", datadir);
- log_info(LD_DIRSERV,"Loading approved fingerprints from \"%s\"...",keydir);
- if (dirserv_parse_fingerprint_file(keydir) < 0) {
+ if (dirserv_load_fingerprint_file() < 0) {
log_err(LD_GENERAL,"Error loading fingerprints");
return -1;
}
@@ -372,8 +364,7 @@ init_keys(void)
(uint16_t)options->DirPort, digest,
options->V1AuthoritativeDir);
}
- /* success */
- return 0;
+ return 0; /* success */
}
/* Keep track of whether we should upload our server descriptor,
@@ -385,13 +376,12 @@ static int can_reach_or_port = 0;
/** Whether we can reach our DirPort from the outside. */
static int can_reach_dir_port = 0;
-/** Return 1 if or port is known reachable; else return 0. */
+/** Return 1 if ORPort is known reachable; else return 0. */
int
check_whether_orport_reachable(void)
{
or_options_t *options = get_options();
- return clique_mode(options) ||
- options->AssumeReachable ||
+ return options->AssumeReachable ||
can_reach_or_port;
}
@@ -446,22 +436,23 @@ decide_to_advertise_dirport(or_options_t *options, routerinfo_t *router)
* Success is noticed in connection_dir_client_reached_eof().
*/
void
-consider_testing_reachability(void)
+consider_testing_reachability(int test_or, int test_dir)
{
routerinfo_t *me = router_get_my_routerinfo();
- if (!me) {
- log_warn(LD_BUG,
- "Bug: router_get_my_routerinfo() did not find my routerinfo?");
+ int orport_reachable = check_whether_orport_reachable();
+ if (!me)
return;
- }
- if (!check_whether_orport_reachable()) {
+ if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
+ log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
+ !orport_reachable ? "reachability" : "bandwidth",
+ me->address, me->or_port);
log_info(LD_CIRC, "Testing reachability of my ORPort: %s:%d.",
me->address, me->or_port);
circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me, 0, 1, 1);
}
- if (!check_whether_dirport_reachable()) {
+ if (test_dir && !check_whether_dirport_reachable()) {
/* ask myself, via tor, for my server descriptor. */
directory_initiate_command_router(me, DIR_PURPOSE_FETCH_SERVERDESC,
1, "authority", NULL, 0);
@@ -473,14 +464,12 @@ void
router_orport_found_reachable(void)
{
if (!can_reach_or_port) {
- if (!clique_mode(get_options()))
- log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
- "the outside. Excellent.%s",
- get_options()->PublishServerDescriptor ?
- " Publishing server descriptor." : "");
+ log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
+ "the outside. Excellent.%s",
+ get_options()->PublishServerDescriptor ?
+ " Publishing server descriptor." : "");
can_reach_or_port = 1;
mark_my_descriptor_dirty();
- consider_publishable_server(time(NULL), 1);
}
}
@@ -492,19 +481,53 @@ router_dirport_found_reachable(void)
log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable "
"from the outside. Excellent.");
can_reach_dir_port = 1;
+ mark_my_descriptor_dirty();
}
}
+#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60)
+
/** Our router has just moved to a new IP. Reset stats. */
void
server_has_changed_ip(void)
{
+ if (stats_n_seconds_working > UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST)
+ reset_bandwidth_test();
stats_n_seconds_working = 0;
can_reach_or_port = 0;
can_reach_dir_port = 0;
mark_my_descriptor_dirty();
}
+/** We have enough testing circuit open. Send a bunch of "drop"
+ * cells down each of them, to exercise our bandwidth. */
+void
+router_perform_bandwidth_test(int num_circs, time_t now)
+{
+ int num_cells = get_options()->BandwidthRate * 10 / CELL_NETWORK_SIZE;
+ int max_cells = num_cells < CIRCWINDOW_START ?
+ num_cells : CIRCWINDOW_START;
+ int cells_per_circuit = max_cells / num_circs;
+ origin_circuit_t *circ = NULL;
+
+ log_notice(LD_OR,"Performing bandwidth self-test.");
+ while ((circ = circuit_get_next_by_pk_and_purpose(circ, NULL,
+ CIRCUIT_PURPOSE_TESTING))) {
+ /* dump cells_per_circuit drop cells onto this circ */
+ int i = cells_per_circuit;
+ if (circ->_base.state != CIRCUIT_STATE_OPEN)
+ continue;
+ circ->_base.timestamp_dirty = now;
+ while (i-- > 0) {
+ if (connection_edge_send_command(NULL, TO_CIRCUIT(circ),
+ RELAY_COMMAND_DROP,
+ NULL, 0, circ->cpath->prev)<0) {
+ return; /* stop if error */
+ }
+ }
+ }
+}
+
/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
@@ -569,7 +592,7 @@ proxy_mode(or_options_t *options)
* - We have the AuthoritativeDirectory option set.
*/
static int
-decide_if_publishable_server(time_t now)
+decide_if_publishable_server(void)
{
or_options_t *options = get_options();
@@ -593,7 +616,7 @@ decide_if_publishable_server(time_t now)
* determine what IP address and ports to test.
*/
void
-consider_publishable_server(time_t now, int force)
+consider_publishable_server(int force)
{
int rebuilt;
@@ -601,7 +624,7 @@ consider_publishable_server(time_t now, int force)
return;
rebuilt = router_rebuild_descriptor(0);
- if (decide_if_publishable_server(now)) {
+ if (decide_if_publishable_server()) {
set_server_advertised(1);
if (rebuilt == 0)
router_upload_dir_desc_to_dirservers(force);
@@ -611,58 +634,9 @@ consider_publishable_server(time_t now, int force)
}
/*
- * Clique maintenance
+ * Clique maintenance -- to be phased out.
*/
-/** OR only: if in clique mode, try to open connections to all of the
- * other ORs we know about. Otherwise, open connections to those we
- * think are in clique mode.
- *
- * If <b>testing_reachability</b> is 0, try to open the connections
- * but only if we don't already have one. If it's 1, then we're an
- * auth dir server, and we should try to connect regardless of
- * whether we already have a connection open -- but if <b>try_all</b>
- * is 0, we want to load balance such that we only try a few connections
- * per call.
- *
- * The load balancing is such that if we get called once every ten
- * seconds, we will cycle through all the tests in 1280 seconds (a
- * bit over 20 minutes).
- */
-void
-router_retry_connections(int testing_reachability, int try_all)
-{
- time_t now = time(NULL);
- routerlist_t *rl = router_get_routerlist();
- or_options_t *options = get_options();
- static char ctr = 0;
-
- tor_assert(server_mode(options));
-
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, {
- const char *id_digest = router->cache_info.identity_digest;
- if (router_is_me(router))
- continue;
- if (!clique_mode(options) && !router_is_clique_mode(router))
- continue;
- if ((testing_reachability &&
- (try_all || (((uint8_t)id_digest[0]) % 128) == ctr)) ||
- (!testing_reachability &&
- !connection_or_get_by_identity_digest(id_digest))) {
- log_debug(LD_OR,"%sconnecting to %s at %s:%u.",
- clique_mode(options) ? "(forced) " : "",
- router->nickname, router->address, router->or_port);
- /* Remember when we started trying to determine reachability */
- if (!router->testing_since)
- router->testing_since = now;
- connection_or_connect(router->addr, router->or_port,
- id_digest);
- }
- });
- if (testing_reachability && !try_all) /* increment ctr */
- ctr = (ctr + 1) % 128;
-}
-
/** Return true iff this OR should try to keep connections open to all
* other ORs. */
int
@@ -696,7 +670,7 @@ router_upload_dir_desc_to_dirservers(int force)
s = router_get_my_descriptor();
if (!s) {
- log_warn(LD_GENERAL, "No descriptor; skipping upload");
+ log_info(LD_GENERAL, "No descriptor; skipping upload");
return;
}
if (!get_options()->PublishServerDescriptor)
@@ -711,16 +685,16 @@ router_upload_dir_desc_to_dirservers(int force)
* conn. Return 0 if we accept; non-0 if we reject.
*/
int
-router_compare_to_my_exit_policy(connection_t *conn)
+router_compare_to_my_exit_policy(edge_connection_t *conn)
{
tor_assert(desc_routerinfo);
/* make sure it's resolved to something. this way we can't get a
'maybe' below. */
- if (!conn->addr)
+ if (!conn->_base.addr)
return -1;
- return compare_addr_to_addr_policy(conn->addr, conn->port,
+ return compare_addr_to_addr_policy(conn->_base.addr, conn->_base.port,
desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED;
}
@@ -777,7 +751,10 @@ router_get_my_descriptor(void)
const char *body;
if (!router_get_my_routerinfo())
return NULL;
+ /* Make sure this is nul-terminated. */
+ tor_assert(desc_routerinfo->cache_info.saved_location == SAVED_NOWHERE);
body = signed_descriptor_get_body(&desc_routerinfo->cache_info);
+ tor_assert(!body[desc_routerinfo->cache_info.signed_descriptor_len]);
log_debug(LD_GENERAL,"my desc is '%s'", body);
return body;
}
@@ -785,9 +762,29 @@ router_get_my_descriptor(void)
/*DOCDOC*/
static smartlist_t *warned_nonexistent_family = NULL;
+static int router_guess_address_from_dir_headers(uint32_t *guess);
+
+/** Return our current best guess at our address, either because
+ * it's configured in torrc, or because we've learned it from
+ * dirserver headers. */
+int
+router_pick_published_address(or_options_t *options, uint32_t *addr)
+{
+ if (resolve_my_address(LOG_INFO, options, addr, NULL) < 0) {
+ log_info(LD_CONFIG, "Could not determine our address locally. "
+ "Checking if directory headers provide any hints.");
+ if (router_guess_address_from_dir_headers(addr) < 0) {
+ log_info(LD_CONFIG, "No hints from directory headers either. "
+ "Will try again later.");
+ return -1;
+ }
+ }
+ return 0;
+}
+
/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild
* a fresh routerinfo and signed server descriptor for this OR.
- * Return 0 on success, -1 on error.
+ * Return 0 on success, -1 on temporary error.
*/
int
router_rebuild_descriptor(int force)
@@ -801,12 +798,16 @@ router_rebuild_descriptor(int force)
if (desc_clean_since && !force)
return 0;
- if (resolve_my_address(options, &addr, NULL) < 0) {
- log_warn(LD_CONFIG,"options->Address didn't resolve into an IP.");
+ if (router_pick_published_address(options, &addr) < 0) {
+ /* Stop trying to rebuild our descriptor every second. We'll
+ * learn that it's time to try again when server_has_changed_ip()
+ * marks it dirty. */
+ desc_clean_since = time(NULL);
return -1;
}
ri = tor_malloc_zero(sizeof(routerinfo_t));
+ ri->routerlist_index = -1;
ri->address = tor_dup_addr(addr);
ri->nickname = tor_strdup(options->Nickname);
ri->addr = addr;
@@ -877,6 +878,11 @@ router_rebuild_descriptor(int force)
}
tor_free(name);
});
+
+ /* remove duplicates from the list */
+ smartlist_sort_strings(ri->declared_family);
+ smartlist_uniq_strings(ri->declared_family);
+
smartlist_free(family);
}
ri->cache_info.signed_descriptor_body = tor_malloc(8192);
@@ -943,6 +949,31 @@ check_descriptor_bandwidth_changed(time_t now)
}
}
+static void
+log_addr_has_changed(int severity, uint32_t prev, uint32_t cur)
+{
+ char addrbuf_prev[INET_NTOA_BUF_LEN];
+ char addrbuf_cur[INET_NTOA_BUF_LEN];
+ struct in_addr in_prev;
+ struct in_addr in_cur;
+
+ in_prev.s_addr = htonl(prev);
+ tor_inet_ntoa(&in_prev, addrbuf_prev, sizeof(addrbuf_prev));
+
+ in_cur.s_addr = htonl(cur);
+ tor_inet_ntoa(&in_cur, addrbuf_cur, sizeof(addrbuf_cur));
+
+ if (prev)
+ log_fn(severity, LD_GENERAL,
+ "Our IP Address has changed from %s to %s; "
+ "rebuilding descriptor.",
+ addrbuf_prev, addrbuf_cur);
+ else
+ log_notice(LD_GENERAL,
+ "Guessed our IP address as %s.",
+ addrbuf_cur);
+}
+
/** Check whether our own address as defined by the Address configuration
* has changed. This is for routers that get their address from a service
* like dyndns. If our address has changed, mark our descriptor dirty. */
@@ -951,36 +982,74 @@ check_descriptor_ipaddress_changed(time_t now)
{
uint32_t prev, cur;
or_options_t *options = get_options();
+ (void) now;
if (!desc_routerinfo)
return;
prev = desc_routerinfo->addr;
- if (resolve_my_address(options, &cur, NULL) < 0) {
- log_warn(LD_CONFIG,"options->Address didn't resolve into an IP.");
+ if (resolve_my_address(LOG_INFO, options, &cur, NULL) < 0) {
+ log_info(LD_CONFIG,"options->Address didn't resolve into an IP.");
return;
}
if (prev != cur) {
- char addrbuf_prev[INET_NTOA_BUF_LEN];
- char addrbuf_cur[INET_NTOA_BUF_LEN];
- struct in_addr in_prev;
- struct in_addr in_cur;
+ log_addr_has_changed(LOG_INFO, prev, cur);
+ mark_my_descriptor_dirty();
+ /* the above call is probably redundant, since resolve_my_address()
+ * probably already noticed and marked it dirty. */
+ }
+}
+
+static uint32_t last_guessed_ip = 0;
+
+/** A directory authority told us our IP address is <b>suggestion</b>.
+ * If this address is different from the one we think we are now, and
+ * if our computer doesn't actually know its IP address, then switch. */
+void
+router_new_address_suggestion(const char *suggestion)
+{
+ uint32_t addr, cur;
+ struct in_addr in;
+ or_options_t *options = get_options();
- in_prev.s_addr = htonl(prev);
- tor_inet_ntoa(&in_prev, addrbuf_prev, sizeof(addrbuf_prev));
+ /* first, learn what the IP address actually is */
+ if (!tor_inet_aton(suggestion, &in)) {
+ log_debug(LD_DIR, "Malformed X-Your-Address-Is header. Ignoring.");
+ return;
+ }
+ addr = ntohl(in.s_addr);
- in_cur.s_addr = htonl(cur);
- tor_inet_ntoa(&in_cur, addrbuf_cur, sizeof(addrbuf_cur));
+ log_debug(LD_DIR, "Got X-Your-Address-Is: %s.", suggestion);
- log_info(LD_GENERAL,
- "Our IP Address has changed from %s to %s; "
- "rebuilding descriptor.",
- addrbuf_prev, addrbuf_cur);
- mark_my_descriptor_dirty();
+ if (!server_mode(options) ||
+ resolve_my_address(LOG_INFO, options, &cur, NULL) >= 0) {
+ /* We're all set -- we already know our address. Great. */
+ last_guessed_ip = cur; /* store it in case we need it later */
+ return;
+ }
+
+ if (last_guessed_ip != addr) {
+ log_addr_has_changed(LOG_NOTICE, last_guessed_ip, addr);
+ server_has_changed_ip();
+ last_guessed_ip = addr; /* router_rebuild_descriptor() will fetch it */
}
}
+/** We failed to resolve our address locally, but we'd like to build
+ * a descriptor and publish / test reachability. If we have a guess
+ * about our address based on directory headers, answer it and return
+ * 0; else return -1. */
+static int
+router_guess_address_from_dir_headers(uint32_t *guess)
+{
+ if (last_guessed_ip) {
+ *guess = last_guessed_ip;
+ return 0;
+ }
+ return -1;
+}
+
/** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short
* string describing the version of Tor and the operating system we're
* currently running on.
@@ -1082,7 +1151,13 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
"uptime %ld\n"
"bandwidth %d %d %d\n"
"onion-key\n%s"
- "signing-key\n%s%s%s%s",
+ "signing-key\n%s"
+#ifdef USE_EVENTDNS
+ "opt eventdns 1\n"
+#else
+ "opt eventdns 0\n"
+#endif
+ "%s%s%s",
router->nickname,
router->address,
router->or_port,
@@ -1161,6 +1236,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
written += result;
}
} /* end for */
+
if (written+256 > maxlen) /* Not enough room for signature. */
return -1;
@@ -1186,7 +1262,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
cp = s_tmp = s_dup = tor_strdup(s);
- ri_tmp = router_parse_entry_from_string(cp, NULL);
+ ri_tmp = router_parse_entry_from_string(cp, NULL, 1);
if (!ri_tmp) {
log_err(LD_BUG,
"We just generated a router descriptor we can't parse: <<%s>>",
@@ -1229,10 +1305,10 @@ is_legal_hexdigest(const char *s)
{
size_t len;
tor_assert(s);
+ if (s[0] == '$') s++;
len = strlen(s);
- return (len == HEX_DIGEST_LEN+1 &&
- s[0] == '$' &&
- strspn(s+1,HEX_CHARACTERS)==len-1);
+ return (len == HEX_DIGEST_LEN &&
+ strspn(s,HEX_CHARACTERS)==len);
}
/** Forget that we have issued any router-related warnings, so that we'll
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index a68ed86e6c..71cc367af1 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -55,9 +55,13 @@ static routerlist_t *routerlist = NULL;
* about. This list is kept sorted by published_on. */
static smartlist_t *networkstatus_list = NULL;
-/** Global list of local_routerstatus_t for each router, known or unknown. */
+/** Global list of local_routerstatus_t for each router, known or unknown.
+ * Kept sorted by digest. */
static smartlist_t *routerstatus_list = NULL;
+/** Map from lowercase nickname to digest of named server, if any. */
+static strmap_t *named_server_map = NULL;
+
/** True iff any member of networkstatus_list has changed since the last time
* we called routerstatus_list_update_from_networkstatus(). */
static int networkstatus_list_has_changed = 0;
@@ -183,17 +187,33 @@ router_append_to_journal(signed_descriptor_t *desc)
tor_assert(len == strlen(body));
- if (append_bytes_to_file(fname, body, len, 0)) {
+ if (append_bytes_to_file(fname, body, len, 1)) {
log_warn(LD_FS, "Unable to store router descriptor");
tor_free(fname);
return -1;
}
+ desc->saved_location = SAVED_IN_JOURNAL;
+ desc->saved_offset = router_journal_len;
tor_free(fname);
router_journal_len += len;
return 0;
}
+static int
+_compare_old_routers_by_age(const void **_a, const void **_b)
+{
+ const signed_descriptor_t *r1 = *_a, *r2 = *_b;
+ return r1->published_on - r2->published_on;
+}
+
+static int
+_compare_routers_by_age(const void **_a, const void **_b)
+{
+ const routerinfo_t *r1 = *_a, *r2 = *_b;
+ return r1->cache_info.published_on - r2->cache_info.published_on;
+}
+
/** If the journal is too long, or if <b>force</b> is true, then atomically
* replace the router store with the routers currently in our routerlist, and
* clear the journal. Return 0 on success, -1 on failure.
@@ -207,6 +227,8 @@ router_rebuild_store(int force)
smartlist_t *chunk_list = NULL;
char *fname = NULL;
int r = -1, i;
+ off_t offset = 0;
+ smartlist_t *old_routers, *routers;
if (!force && !router_should_rebuild_store())
return 0;
@@ -216,15 +238,28 @@ router_rebuild_store(int force)
/* Don't save deadweight. */
routerlist_remove_old_routers();
+ log_info(LD_DIR, "Rebuilding router descriptor cache");
+
options = get_options();
fname_len = strlen(options->DataDirectory)+32;
fname = tor_malloc(fname_len);
tor_snprintf(fname, fname_len, "%s/cached-routers", options->DataDirectory);
chunk_list = smartlist_create();
+ old_routers = smartlist_create();
+ smartlist_add_all(old_routers, routerlist->old_routers);
+ smartlist_sort(old_routers, _compare_old_routers_by_age);
+ routers = smartlist_create();
+ smartlist_add_all(routers, routerlist->routers);
+ smartlist_sort(routers, _compare_routers_by_age);
for (i = 0; i < 2; ++i) {
- smartlist_t *lst = (i == 0) ? routerlist->old_routers :
- routerlist->routers;
+ smartlist_t *lst = smartlist_create();
+ /* We sort the routers by age to enhance locality on disk. */
+ if (i==0)
+ lst = old_routers;
+ else
+ lst = routers;
+ /* Now, add the appropriate members to chunk_list */
SMARTLIST_FOREACH(lst, void *, ptr,
{
signed_descriptor_t *sd = (i==0) ?
@@ -233,6 +268,7 @@ router_rebuild_store(int force)
const char *body = signed_descriptor_get_body(sd);
if (!body) {
log_warn(LD_BUG, "Bug! No descriptor available for router.");
+ smartlist_free(lst);
goto done;
}
c = tor_malloc(sizeof(sized_chunk_t));
@@ -241,15 +277,42 @@ router_rebuild_store(int force)
smartlist_add(chunk_list, c);
});
}
- if (write_chunks_to_file(fname, chunk_list, 0)<0) {
+ if (write_chunks_to_file(fname, chunk_list, 1)<0) {
log_warn(LD_FS, "Error writing router store to disk.");
goto done;
}
+ /* Our mmap is now invalid. */
+ if (routerlist->mmap_descriptors) {
+ tor_munmap_file(routerlist->mmap_descriptors);
+ routerlist->mmap_descriptors = tor_mmap_file(fname);
+ if (! routerlist->mmap_descriptors)
+ log_warn(LD_FS, "Unable to mmap new descriptor file at '%s'.",fname);
+ }
+
+ offset = 0;
+ for (i = 0; i < 2; ++i) {
+ smartlist_t *lst = (i == 0) ? old_routers : routers;
+ SMARTLIST_FOREACH(lst, void *, ptr,
+ {
+ signed_descriptor_t *sd = (i==0) ?
+ ((signed_descriptor_t*)ptr): &((routerinfo_t*)ptr)->cache_info;
+
+ sd->saved_location = SAVED_IN_CACHE;
+ if (routerlist->mmap_descriptors) {
+ tor_free(sd->signed_descriptor_body); // sets it to null
+ sd->saved_offset = offset;
+ }
+ offset += sd->signed_descriptor_len;
+ signed_descriptor_get_body(sd);
+ });
+ }
+ smartlist_free(old_routers);
+ smartlist_free(routers);
tor_snprintf(fname, fname_len, "%s/cached-routers.new",
options->DataDirectory);
- write_str_to_file(fname, "", 0);
+ write_str_to_file(fname, "", 1);
r = 0;
router_store_len = len;
@@ -272,31 +335,36 @@ router_reload_router_list(void)
{
or_options_t *options = get_options();
size_t fname_len = strlen(options->DataDirectory)+32;
- char *fname = tor_malloc(fname_len);
+ char *fname = tor_malloc(fname_len), *contents;
struct stat st;
- int j;
if (!routerlist)
router_get_routerlist(); /* mallocs and inits it in place */
router_journal_len = router_store_len = 0;
- for (j = 0; j < 2; ++j) {
- char *contents;
- tor_snprintf(fname, fname_len,
- (j==0)?"%s/cached-routers":"%s/cached-routers.new",
- options->DataDirectory);
- contents = read_file_to_str(fname, 0);
- if (contents) {
- stat(fname, &st);
- if (j==0)
- router_store_len = st.st_size;
- else
- router_journal_len = st.st_size;
- router_load_routers_from_string(contents, 1, NULL);
- tor_free(contents);
- }
+ tor_snprintf(fname, fname_len, "%s/cached-routers", options->DataDirectory);
+
+ if (routerlist->mmap_descriptors) /* get rid of it first */
+ tor_munmap_file(routerlist->mmap_descriptors);
+
+ routerlist->mmap_descriptors = tor_mmap_file(fname);
+ if (routerlist->mmap_descriptors) {
+ router_store_len = routerlist->mmap_descriptors->size;
+ router_load_routers_from_string(routerlist->mmap_descriptors->data,
+ SAVED_IN_CACHE, NULL);
}
+
+ tor_snprintf(fname, fname_len, "%s/cached-routers.new",
+ options->DataDirectory);
+ contents = read_file_to_str(fname, 1);
+ if (contents) {
+ stat(fname, &st);
+ router_load_routers_from_string(contents,
+ SAVED_IN_JOURNAL, NULL);
+ tor_free(contents);
+ }
+
tor_free(fname);
if (router_journal_len) {
@@ -538,6 +606,21 @@ router_reset_status_download_failures(void)
mark_all_trusteddirservers_up();
}
+/** Look through the routerlist and identify routers that
+ * advertise the same /16 network address as <b>router</b>.
+ * Add each of them to <b>sl</b>.
+ */
+static void
+routerlist_add_network_family(smartlist_t *sl, routerinfo_t *router)
+{
+ SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r,
+ {
+ if (router != r &&
+ (router->addr & 0xffff0000) == (r->addr & 0xffff0000))
+ smartlist_add(sl, r);
+ });
+}
+
/** Add all the family of <b>router</b> to the smartlist <b>sl</b>.
* This is used to make sure we don't pick siblings in a single path.
*/
@@ -547,6 +630,10 @@ routerlist_add_family(smartlist_t *sl, routerinfo_t *router)
routerinfo_t *r;
config_line_t *cl;
+ /* First, add any routers with similar network addresses.
+ * XXX It's possible this will be really expensive; we'll see. */
+ routerlist_add_network_family(sl, router);
+
if (!router->declared_family)
return;
@@ -931,6 +1018,7 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
char digest[DIGEST_LEN];
routerinfo_t *best_match=NULL;
int n_matches = 0;
+ char *named_digest = NULL;
tor_assert(nickname);
if (!routerlist)
@@ -944,16 +1032,17 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
maybedigest = (strlen(nickname) == HEX_DIGEST_LEN) &&
(base16_decode(digest,DIGEST_LEN,nickname,HEX_DIGEST_LEN) == 0);
+ if (named_server_map &&
+ (named_digest = strmap_get_lc(named_server_map, nickname))) {
+ return digestmap_get(routerlist->identity_map, named_digest);
+ }
+
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
{
if (!strcasecmp(router->nickname, nickname)) {
- if (router->is_named)
- return router;
- else {
- ++n_matches;
- if (n_matches <= 1 || router->is_running)
- best_match = router;
- }
+ ++n_matches;
+ if (n_matches <= 1 || router->is_running)
+ best_match = router;
} else if (maybedigest &&
!memcmp(digest, router->cache_info.identity_digest, DIGEST_LEN)
) {
@@ -991,7 +1080,7 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
char *alternatives = smartlist_join_strings(fps, "; ",0,NULL);
log_warn(LD_CONFIG,
"There are multiple matches for the nickname \"%s\","
- " but none is listed as named by the directory authories. "
+ " but none is listed as named by the directory authorities. "
"Choosing one arbitrarily. If you meant one in particular, "
"you should say %s.", nickname, alternatives);
tor_free(alternatives);
@@ -1006,7 +1095,7 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
base16_encode(fp, sizeof(fp),
best_match->cache_info.identity_digest, DIGEST_LEN);
log_warn(LD_CONFIG, "You specified a server \"%s\" by name, but the "
- "directory authorities do not have a listing for this name. "
+ "directory authorities do not have a binding for this nickname. "
"To make sure you get the same server in the future, refer to "
"it by key, as \"$%s\".", nickname, fp);
rs->name_lookup_warned = 1;
@@ -1090,10 +1179,28 @@ router_get_by_descriptor_digest(const char *digest)
return digestmap_get(routerlist->desc_digest_map, digest);
}
+/* DOCDOC Not always nul-terminated. */
const char *
signed_descriptor_get_body(signed_descriptor_t *desc)
{
- return desc->signed_descriptor_body;
+ const char *r;
+ size_t len = desc->signed_descriptor_len;
+ tor_assert(len > 32);
+ if (desc->saved_location == SAVED_IN_CACHE && routerlist &&
+ routerlist->mmap_descriptors) {
+ tor_assert(desc->saved_offset + len <= routerlist->mmap_descriptors->size);
+ r = routerlist->mmap_descriptors->data + desc->saved_offset;
+ } else {
+ r = desc->signed_descriptor_body;
+ }
+ tor_assert(r);
+ tor_assert(!memcmp("router ", r, 7));
+#if 0
+ tor_assert(!memcmp("\n-----END SIGNATURE-----\n",
+ r + len - 25, 25));
+#endif
+
+ return r;
}
/** Return the current list of all known routers. */
@@ -1167,7 +1274,10 @@ routerlist_free(routerlist_t *rl)
signed_descriptor_free(sd));
smartlist_free(rl->routers);
smartlist_free(rl->old_routers);
+ if (routerlist->mmap_descriptors)
+ tor_munmap_file(routerlist->mmap_descriptors);
tor_free(rl);
+
router_dir_info_changed();
}
@@ -1239,6 +1349,7 @@ routerlist_insert(routerlist_t *rl, routerinfo_t *ri)
digestmap_set(rl->desc_digest_map, ri->cache_info.signed_descriptor_digest,
&(ri->cache_info));
smartlist_add(rl->routers, ri);
+ ri->routerlist_index = smartlist_len(rl->routers) - 1;
router_dir_info_changed();
// routerlist_assert_ok(rl);
}
@@ -1252,6 +1363,7 @@ routerlist_insert_old(routerlist_t *rl, routerinfo_t *ri)
signed_descriptor_t *sd = signed_descriptor_from_routerinfo(ri);
digestmap_set(rl->desc_digest_map, sd->signed_descriptor_digest, sd);
smartlist_add(rl->old_routers, sd);
+ ri->routerlist_index = -1;
} else {
routerinfo_free(ri);
}
@@ -1270,7 +1382,13 @@ routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int idx, int make_old)
idx = _routerlist_find_elt(rl->routers, ri, idx);
if (idx < 0)
return;
+ ri->routerlist_index = -1;
smartlist_del(rl->routers, idx);
+ if (idx < smartlist_len(rl->routers)) {
+ routerinfo_t *r = smartlist_get(rl->routers, idx);
+ r->routerlist_index = idx;
+ }
+
ri_tmp = digestmap_remove(rl->identity_map, ri->cache_info.identity_digest);
router_dir_info_changed();
tor_assert(ri_tmp == ri);
@@ -1319,6 +1437,8 @@ routerlist_replace(routerlist_t *rl, routerinfo_t *ri_old,
router_dir_info_changed();
if (idx >= 0) {
smartlist_set(rl->routers, idx, ri_new);
+ ri_old->routerlist_index = -1;
+ ri_new->routerlist_index = idx;
} else {
log_warn(LD_BUG, "Appending entry from routerlist_replace.");
routerlist_insert(rl, ri_new);
@@ -1502,10 +1622,10 @@ int
router_add_to_routerlist(routerinfo_t *router, const char **msg,
int from_cache, int from_fetch)
{
- int i;
const char *id_digest;
int authdir = get_options()->AuthoritativeDir;
int authdir_believes_valid = 0;
+ routerinfo_t *old_router;
tor_assert(msg);
@@ -1561,68 +1681,91 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
rs->need_to_mirror = 0;
});
- /* If we have a router with this name, and the identity key is the same,
- * choose the newer one. If the identity key has changed, and one of the
+ /* If we have a router with the same identity key, choose the newer one. */
+ old_router = digestmap_get(routerlist->identity_map,
+ router->cache_info.identity_digest);
+ if (old_router) {
+ int pos = old_router->routerlist_index;
+ tor_assert(smartlist_get(routerlist->routers, pos) == old_router);
+
+ if (router->cache_info.published_on <=
+ old_router->cache_info.published_on) {
+ /* Same key, but old */
+ log_debug(LD_DIR, "Skipping not-new descriptor for router '%s'",
+ router->nickname);
+ /* Only journal this desc if we'll be serving it. */
+ if (!from_cache && get_options()->DirPort)
+ router_append_to_journal(&router->cache_info);
+ routerlist_insert_old(routerlist, router);
+ *msg = "Router descriptor was not new.";
+ return -1;
+ } else {
+ /* Same key, new. */
+ int unreachable = 0;
+ log_debug(LD_DIR, "Replacing entry for router '%s/%s' [%s]",
+ router->nickname, old_router->nickname,
+ hex_str(id_digest,DIGEST_LEN));
+ if (router->addr == old_router->addr &&
+ router->or_port == old_router->or_port) {
+ /* these carry over when the address and orport are unchanged.*/
+ router->last_reachable = old_router->last_reachable;
+ router->testing_since = old_router->testing_since;
+ router->num_unreachable_notifications =
+ old_router->num_unreachable_notifications;
+ }
+ if (authdir && !from_cache && !from_fetch &&
+ router_have_minimum_dir_info() &&
+ dirserv_thinks_router_is_blatantly_unreachable(router,
+ time(NULL))) {
+ if (router->num_unreachable_notifications >= 3) {
+ unreachable = 1;
+ log_notice(LD_DIR, "Notifying server '%s' that it's unreachable. "
+ "(ContactInfo '%s', platform '%s').",
+ router->nickname,
+ router->contact_info ? router->contact_info : "",
+ router->platform ? router->platform : "");
+ } else {
+ log_info(LD_DIR,"'%s' may be unreachable -- the %d previous "
+ "descriptors were thought to be unreachable.",
+ router->nickname, router->num_unreachable_notifications);
+ router->num_unreachable_notifications++;
+ }
+ }
+ routerlist_replace(routerlist, old_router, router, pos, 1);
+ if (!from_cache) {
+ router_append_to_journal(&router->cache_info);
+ }
+ directory_set_dirty();
+ *msg = unreachable ? "Dirserver believes your ORPort is unreachable" :
+ authdir_believes_valid ? "Valid server updated" :
+ ("Invalid server updated. (This dirserver is marking your "
+ "server as unapproved.)");
+ return unreachable ? 1 : 0;
+ }
+ }
+
+#if 0
+ /* XXXX This block is slow, and could be smarter. All it does is ensure
+ * that if we have a named server called "Foo", we will never have another
+ * server called "Foo." router_get_by_nickname() already knows to prefer
+ * named routers, so the problem only arises when there is a named router
+ * called 'foo', but we don't have it. If, instead, we kept a
+ * name-to-identity-key mapping for each named router in the networkstatus
+ * list, we could eliminate this block.
+ *
+ * Hm. perhaps we should; I don't see how this code is non-broken wrt named
+ * routers. -NM
+ */
+
+ /* If the identity key has changed, and one of the
* routers is named, drop the unnamed ones. (If more than one are named,
* drop the old ones.)
*/
for (i = 0; i < smartlist_len(routerlist->routers); ++i) {
routerinfo_t *old_router = smartlist_get(routerlist->routers, i);
- if (!crypto_pk_cmp_keys(router->identity_pkey,old_router->identity_pkey)) {
- if (router->cache_info.published_on <=
- old_router->cache_info.published_on) {
- /* Same key, but old */
- log_debug(LD_DIR, "Skipping not-new descriptor for router '%s'",
- router->nickname);
- /* Only journal this desc if we'll be serving it. */
- if (!from_cache && get_options()->DirPort)
- router_append_to_journal(&router->cache_info);
- routerlist_insert_old(routerlist, router);
- *msg = "Router descriptor was not new.";
- return -1;
- } else {
- /* Same key, new. */
- int unreachable = 0;
- log_debug(LD_DIR, "Replacing entry for router '%s/%s' [%s]",
- router->nickname, old_router->nickname,
- hex_str(id_digest,DIGEST_LEN));
- if (router->addr == old_router->addr &&
- router->or_port == old_router->or_port) {
- /* these carry over when the address and orport are unchanged.*/
- router->last_reachable = old_router->last_reachable;
- router->testing_since = old_router->testing_since;
- router->num_unreachable_notifications =
- old_router->num_unreachable_notifications;
- }
- if (authdir && !from_cache && !from_fetch &&
- router_have_minimum_dir_info() &&
- dirserv_thinks_router_is_blatantly_unreachable(router,
- time(NULL))) {
- if (router->num_unreachable_notifications >= 3) {
- unreachable = 1;
- log_notice(LD_DIR, "Notifying server '%s' that it's unreachable. "
- "(ContactInfo '%s', platform '%s').",
- router->nickname,
- router->contact_info ? router->contact_info : "",
- router->platform ? router->platform : "");
- } else {
- log_info(LD_DIR,"'%s' may be unreachable -- the %d previous "
- "descriptors were thought to be unreachable.",
- router->nickname, router->num_unreachable_notifications);
- router->num_unreachable_notifications++;
- }
- }
- routerlist_replace(routerlist, old_router, router, i, 1);
- if (!from_cache) {
- router_append_to_journal(&router->cache_info);
- }
- directory_set_dirty();
- *msg = unreachable ? "Dirserver believes your ORPort is unreachable" :
- authdir_believes_valid ? "Valid server updated" :
- ("Invalid server updated. (This dirserver is marking your "
- "server as unapproved.)");
- return unreachable ? 1 : 0;
- }
+ if (!memcmp(router->cache_info.identity_digest,
+ old_router->cache_info.identity_digest, DIGEST_LEN)) {
+
} else if (!strcasecmp(router->nickname, old_router->nickname)) {
/* nicknames match, keys don't. */
if (router->is_named) {
@@ -1633,13 +1776,13 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
/* mark-for-close connections using the old key, so we can
* make new ones with the new key.
*/
- connection_t *conn;
+ or_connection_t *conn;
while ((conn = connection_or_get_by_identity_digest(
old_router->cache_info.identity_digest))) {
log_info(LD_DIR,"Closing conn to router '%s'; there is now a named "
"router with that name.",
old_router->nickname);
- connection_mark_for_close(conn);
+ connection_mark_for_close(TO_CONN(conn));
}
routerlist_remove(routerlist, old_router, i--, 0);
} else if (old_router->is_named) {
@@ -1653,6 +1796,8 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
}
}
}
+#endif
+
/* We haven't seen a router with this name before. Add it to the end of
* the list. */
routerlist_insert(routerlist, router);
@@ -1790,7 +1935,7 @@ routerlist_remove_old_routers(void)
retain = digestmap_new();
cutoff = now - OLD_ROUTER_DESC_MAX_AGE;
- if (server_mode(options) && options->DirPort) {
+ if (options->DirPort) {
SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
{
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
@@ -1879,7 +2024,7 @@ router_load_single_router(const char *s, uint8_t purpose, const char **msg)
tor_assert(msg);
*msg = NULL;
- if (!(ri = router_parse_entry_from_string(s, NULL))) {
+ if (!(ri = router_parse_entry_from_string(s, NULL, 1))) {
log_warn(LD_DIR, "Error parsing router descriptor; dropping.");
*msg = "Couldn't parse router descriptor.";
return -1;
@@ -1919,16 +2064,18 @@ router_load_single_router(const char *s, uint8_t purpose, const char **msg)
* uppercased identity fingerprints. Do not update any router whose
* fingerprint is not on the list; after updating a router, remove its
* fingerprint from the list.
+ * DOCDOC saved_location
*/
void
-router_load_routers_from_string(const char *s, int from_cache,
+router_load_routers_from_string(const char *s, saved_location_t saved_location,
smartlist_t *requested_fingerprints)
{
smartlist_t *routers = smartlist_create(), *changed = smartlist_create();
char fp[HEX_DIGEST_LEN+1];
const char *msg;
+ int from_cache = (saved_location != SAVED_NOWHERE);
- router_parse_list_from_string(&s, routers);
+ router_parse_list_from_string(&s, routers, saved_location);
routers_update_status_from_networkstatus(routers, !from_cache);
@@ -2023,12 +2170,13 @@ add_networkstatus_to_cache(const char *s,
/** How far in the future do we allow a network-status to get before removing
* it? (seconds) */
#define NETWORKSTATUS_ALLOW_SKEW (24*60*60)
+
/** Given a string <b>s</b> containing a network status that we received at
* <b>arrived_at</b> from <b>source</b>, try to parse it, see if we want to
* store it, and put it into our cache as necessary.
*
* If <b>source</b> is NS_FROM_DIR or NS_FROM_CACHE, do not replace our
- * own networkstatus_t (if we're a directory server).
+ * own networkstatus_t (if we're an authoritative directory server).
*
* If <b>source</b> is NS_FROM_CACHE, do not write our networkstatus_t to the
* cache.
@@ -2093,7 +2241,8 @@ router_set_networkstatus(const char *s, time_t arrived_at,
if (!networkstatus_list)
networkstatus_list = smartlist_create();
- if (source == NS_FROM_DIR && router_digest_is_me(ns->identity_digest)) {
+ if ( (source == NS_FROM_DIR_BY_FP || source == NS_FROM_DIR_ALL) &&
+ router_digest_is_me(ns->identity_digest)) {
/* Don't replace our own networkstatus when we get it from somebody else.*/
networkstatus_free(ns);
return 0;
@@ -2105,12 +2254,14 @@ router_set_networkstatus(const char *s, time_t arrived_at,
} else {
char *requested =
smartlist_join_strings(requested_fingerprints," ",0,NULL);
- log_warn(LD_DIR,
+ if (source != NS_FROM_DIR_ALL) {
+ log_warn(LD_DIR,
"We received a network status with a fingerprint (%s) that we "
"never requested. (We asked for: %s.) Dropping.",
fp, requested);
- tor_free(requested);
- return 0;
+ tor_free(requested);
+ return 0;
+ }
}
}
@@ -2119,6 +2270,9 @@ router_set_networkstatus(const char *s, time_t arrived_at,
/* We got a non-trusted networkstatus, and we're a directory cache.
* This means that we asked an authority, and it told us about another
* authority we didn't recognize. */
+ log_info(LD_DIR,
+ "We do not recognize authority (%s) but we are willing "
+ "to cache it", fp);
add_networkstatus_to_cache(s, source, ns);
networkstatus_free(ns);
}
@@ -2181,7 +2335,8 @@ router_set_networkstatus(const char *s, time_t arrived_at,
log_info(LD_DIR, "Setting networkstatus %s %s (published %s)",
source == NS_FROM_CACHE?"cached from":
- (source==NS_FROM_DIR?"downloaded from":"generated for"),
+ ((source == NS_FROM_DIR_BY_FP || source == NS_FROM_DIR_ALL) ?
+ "downloaded from":"generated for"),
trusted_dir->description, published);
networkstatus_list_has_changed = 1;
router_dir_info_changed();
@@ -2501,6 +2656,7 @@ update_networkstatus_client_downloads(time_t now)
resource = tor_malloc(resource_len);
memcpy(resource, "fp/", 3);
cp = resource+3;
+ smartlist_sort_digests(missing);
needed = smartlist_len(missing);
SMARTLIST_FOREACH(missing, const char *, d,
{
@@ -2521,10 +2677,10 @@ void
update_networkstatus_downloads(time_t now)
{
or_options_t *options = get_options();
- if (server_mode(options) && options->DirPort)
- update_networkstatus_cache_downloads(time(NULL));
- else
- update_networkstatus_client_downloads(time(NULL));
+ if (options->DirPort)
+ update_networkstatus_cache_downloads(now);
+ else
+ update_networkstatus_client_downloads(now);
}
/** Return 1 if all running sufficiently-stable routers will reject
@@ -2572,7 +2728,7 @@ add_trusted_dir_server(const char *nickname, const char *address,
trusted_dir_servers = smartlist_create();
if (!address) { /* The address is us; we should guess. */
- if (resolve_my_address(get_options(), &a, &hostname) < 0) {
+ if (resolve_my_address(LOG_WARN, get_options(), &a, &hostname) < 0) {
log_warn(LD_CONFIG,
"Couldn't find a suitable address when adding ourself as a "
"trusted directory server.");
@@ -2681,6 +2837,7 @@ compute_recommended_versions(time_t now, int client)
smartlist_t *combined, *recommended;
int n_versioning;
char *result;
+ (void) now; /* right now, we consider *all* ors. */
if (!networkstatus_list)
return tor_strdup("<none>");
@@ -2775,12 +2932,12 @@ routers_update_all_from_networkstatus(void)
++n_named;
});
- if (n_recent && n_listing) {
- if (n_valid <= n_recent/2) {
+ if (n_listing) {
+ if (n_valid <= n_listing/2) {
log_info(LD_GENERAL,
"%d/%d recent statements from directory authorities list us "
"as unapproved. Are you misconfigured?",
- n_recent-n_valid, n_recent);
+ n_listing-n_valid, n_listing);
have_warned_about_invalid_status = 1;
} else if (n_naming && !n_named) {
log_info(LD_GENERAL, "0/%d name-binding directory authorities "
@@ -2981,6 +3138,10 @@ routerstatus_list_update_from_networkstatus(time_t now)
* is a conflict on that nickname, map the lc nickname to conflict.
*/
name_map = strmap_new();
+ /* Clear the global map... */
+ if (named_server_map)
+ strmap_free(named_server_map, _tor_free);
+ named_server_map = strmap_new();
memset(conflict, 0xff, sizeof(conflict));
for (i = 0; i < n_statuses; ++i) {
if (!networkstatus[i]->binds_names)
@@ -2994,11 +3155,14 @@ routerstatus_list_update_from_networkstatus(time_t now)
warned = smartlist_string_isin(warned_conflicts, rs->nickname);
if (!other_digest) {
strmap_set_lc(name_map, rs->nickname, rs->identity_digest);
+ strmap_set_lc(named_server_map, rs->nickname,
+ tor_memdup(rs->identity_digest, DIGEST_LEN));
if (warned)
smartlist_string_remove(warned_conflicts, rs->nickname);
} else if (memcmp(other_digest, rs->identity_digest, DIGEST_LEN) &&
other_digest != conflict) {
if (!warned) {
+ char *d;
int should_warn = options->DirPort && options->AuthoritativeDir;
char fp1[HEX_DIGEST_LEN+1];
char fp2[HEX_DIGEST_LEN+1];
@@ -3009,6 +3173,8 @@ routerstatus_list_update_from_networkstatus(time_t now)
"($%s vs $%s)",
rs->nickname, fp1, fp2);
strmap_set_lc(name_map, rs->nickname, conflict);
+ d = strmap_remove_lc(named_server_map, rs->nickname);
+ tor_free(d);
smartlist_add(warned_conflicts, tor_strdup(rs->nickname));
}
} else {
@@ -3171,6 +3337,7 @@ routerstatus_list_update_from_networkstatus(time_t now)
}
SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, rs,
local_routerstatus_free(rs));
+
smartlist_free(routerstatus_list);
routerstatus_list = result;
@@ -3251,9 +3418,10 @@ list_pending_descriptor_downloads(digestmap_t *result)
if (conn->type == CONN_TYPE_DIR &&
conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC &&
!conn->marked_for_close) {
- if (!strcmpstart(conn->requested_resource, prefix))
- dir_split_resource_into_fingerprints(conn->requested_resource+p_len,
- tmp, NULL, 1);
+ const char *resource = TO_DIR_CONN(conn)->requested_resource;
+ if (!strcmpstart(resource, prefix))
+ dir_split_resource_into_fingerprints(resource + p_len,
+ tmp, NULL, 1, 0);
}
}
SMARTLIST_FOREACH(tmp, char *, d,
@@ -3317,18 +3485,18 @@ initiate_descriptor_downloads(routerstatus_t *source,
/** Return 0 if this routerstatus is obsolete, too new, isn't
* running, or otherwise not a descriptor that we would make any
* use of even if we had it. Else return 1. */
-static int
+static INLINE int
client_would_use_router(routerstatus_t *rs, time_t now, or_options_t *options)
{
- if (rs->published_on + ROUTER_MAX_AGE < now) {
- /* This one is too old to consider. */
- return 0;
- }
if (!rs->is_running && !options->FetchUselessDescriptors) {
/* If we had this router descriptor, we wouldn't even bother using it.
* But, if we want to have a complete list, fetch it anyway. */
return 0;
}
+ if (rs->published_on + ROUTER_MAX_AGE < now) {
+ /* This one is too old to consider. */
+ return 0;
+ }
if (rs->published_on + ESTIMATED_PROPAGATION_TIME > now) {
/* Most caches probably don't have this descriptor yet. */
return 0;
@@ -3360,15 +3528,15 @@ router_list_client_downloadable(void)
SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, rs,
{
routerinfo_t *ri;
- if (!client_would_use_router(&rs->status, now, options)) {
+ if (router_get_by_descriptor_digest(rs->status.descriptor_digest)) {
+ /* We have the 'best' descriptor for this router. */
+ ++n_uptodate;
+ } else if (!client_would_use_router(&rs->status, now, options)) {
/* We wouldn't want this descriptor even if we got it. */
++n_wouldnt_use;
} else if (digestmap_get(downloading, rs->status.descriptor_digest)) {
/* We're downloading this one now. */
++n_in_progress;
- } else if (router_get_by_descriptor_digest(rs->status.descriptor_digest)) {
- /* We have the 'best' descriptor for this router. */
- ++n_uptodate;
} else if ((ri = router_get_by_digest(rs->status.identity_digest)) &&
ri->cache_info.published_on > rs->status.published_on) {
/* Oddly, we have a descriptor more recent than the 'best' one, but it
@@ -3433,15 +3601,19 @@ update_router_descriptor_client_downloads(time_t now)
int should_delay, n_downloadable;
or_options_t *options = get_options();
- if (server_mode(options) && options->DirPort) {
+ if (options->DirPort) {
log_warn(LD_BUG,
- "Called router_descriptor_client_downloads() on a mirror?");
+ "Called router_descriptor_client_downloads() on a dir mirror?");
}
- /* XXX here's another magic 2 that probably should be replaced
- * by <= smartlist_len(trusted_dir_servers)/2
- * or by a function returning same. -- weasel */
- if (networkstatus_list && smartlist_len(networkstatus_list) < 2) {
+ if (rep_hist_circbuilding_dormant(now)) {
+ log_info(LD_CIRC, "Skipping descriptor downloads: we haven't needed "
+ "any circuits lately.");
+ return;
+ }
+
+ if (networkstatus_list && smartlist_len(networkstatus_list) <=
+ smartlist_len(trusted_dir_servers)/2) {
log_info(LD_DIR,
"Not enough networkstatus documents to launch requests.");
}
@@ -3466,8 +3638,8 @@ update_router_descriptor_client_downloads(time_t now)
(int)(now-last_routerdesc_download_attempted));
} else {
log_info(LD_DIR,
- "There are not many downloadable routerdescs, but we've "
- "never downloaded descriptors before. Downloading.");
+ "There are not many downloadable routerdescs, but we haven't "
+ "tried downloading descriptors recently. Downloading.");
}
}
}
@@ -3485,6 +3657,7 @@ update_router_descriptor_client_downloads(time_t now)
(n_downloadable+n_per_request-1)/n_per_request,
n_downloadable>n_per_request?"s":"",
n_downloadable, n_downloadable>1?"s":"", n_per_request);
+ smartlist_sort_digests(downloadable);
for (i=0; i < n_downloadable; i += n_per_request) {
initiate_descriptor_downloads(NULL, downloadable, i, i+n_per_request);
}
@@ -3505,10 +3678,11 @@ update_router_descriptor_cache_downloads(time_t now)
int i, j, n;
int n_download;
or_options_t *options = get_options();
+ (void) now;
- if (!(server_mode(options) && options->DirPort)) {
+ if (!options->DirPort) {
log_warn(LD_BUG, "Called update_router_descriptor_cache_downloads() "
- "on a non-mirror?");
+ "on a non-dir-mirror?");
}
if (!networkstatus_list || !smartlist_len(networkstatus_list))
@@ -3622,7 +3796,7 @@ void
update_router_descriptor_downloads(time_t now)
{
or_options_t *options = get_options();
- if (server_mode(options) && options->DirPort) {
+ if (options->DirPort) {
update_router_descriptor_cache_downloads(now);
} else {
update_router_descriptor_client_downloads(now);
@@ -3659,7 +3833,7 @@ router_have_minimum_dir_info(void)
/** DOCDOC
* Must change when authorities change, networkstatuses change, or list of
- * routerdescs changes.
+ * routerdescs changes, or number of running routers changes.
*/
static void
router_dir_info_changed(void)
@@ -3759,6 +3933,9 @@ router_reset_descriptor_download_failures(void)
* automatically non-cosmetic. */
#define ROUTER_MAX_COSMETIC_TIME_DIFFERENCE (12*60*60)
+/** We allow uptime to vary from how much it ought to be by this much. */
+#define ROUTER_ALLOW_UPTIME_DRIFT (30*60)
+
/** Return true iff the only differences between r1 and r2 are such that
* would not cause a recent (post 0.1.1.6) dirserver to republish.
*/
@@ -3788,6 +3965,7 @@ router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
(r1->contact_info && r2->contact_info &&
strcasecmp(r1->contact_info, r2->contact_info)) ||
r1->is_hibernating != r2->is_hibernating ||
+ r1->has_old_dnsworkers != r2->has_old_dnsworkers ||
cmp_addr_policies(r1->exit_policy, r2->exit_policy))
return 0;
if ((r1->declared_family == NULL) != (r2->declared_family == NULL))
@@ -3818,7 +3996,8 @@ router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
* give or take 30 minutes? */
r1pub = r1->cache_info.published_on;
r2pub = r2->cache_info.published_on;
- if (abs(r2->uptime - (r1->uptime + (r2pub - r1pub))) > 30*60)
+ if (abs(r2->uptime - (r1->uptime + (r2pub - r1pub)))
+ > ROUTER_ALLOW_UPTIME_DRIFT)
return 0;
/* Otherwise, the difference is cosmetic. */
@@ -3840,6 +4019,7 @@ routerlist_assert_ok(routerlist_t *rl)
sd2 = digestmap_get(rl->desc_digest_map,
r->cache_info.signed_descriptor_digest);
tor_assert(&(r->cache_info) == sd2);
+ tor_assert(r->routerlist_index == r_sl_idx);
});
SMARTLIST_FOREACH(rl->old_routers, signed_descriptor_t *, sd,
{
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 10f5512ae3..2f214a1ef4 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -54,6 +54,7 @@ typedef enum {
K_SERVER_VERSIONS,
K_R,
K_S,
+ K_EVENTDNS,
_UNRECOGNIZED,
_ERR,
_EOF,
@@ -145,6 +146,7 @@ static struct {
{ "dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ, NETSTATUS },
{ "client-versions", K_CLIENT_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
{ "server-versions", K_SERVER_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
+ { "eventdns", K_EVENTDNS, ARGS, NO_OBJ, RTR },
{ NULL, -1, NO_ARGS, NO_OBJ, ANY }
};
@@ -540,14 +542,6 @@ find_dir_signing_key(const char *str)
if (tok->key) {
key = tok->key;
tok->key = NULL; /* steal reference. */
- } else if (tok->n_args >= 1) {
- /** XXXX Once all the directories are running 0.1.0.6-rc or later, we
- * can remove this logic. */
- key = crypto_pk_DER64_decode_public_key(tok->args[0]);
- if (!key) {
- log_warn(LD_DIR, "Unparseable dir-signing-key argument");
- return NULL;
- }
} else {
log_warn(LD_DIR, "Dir-signing-key token contained no key");
return NULL;
@@ -643,17 +637,20 @@ check_directory_signature(const char *digest,
* are marked running and valid. Advances *s to a point immediately
* following the last router entry. Ignore any trailing router entries that
* are not complete. Returns 0 on success and -1 on failure.
+ * DOCDOC saved_location
*/
int
-router_parse_list_from_string(const char **s, smartlist_t *dest)
+router_parse_list_from_string(const char **s, smartlist_t *dest,
+ saved_location_t saved_location)
{
routerinfo_t *router;
- const char *end, *cp;
+ const char *end, *cp, *start;
tor_assert(s);
tor_assert(*s);
tor_assert(dest);
+ start = *s;
while (1) {
*s = eat_whitespace(*s);
/* Don't start parsing the rest of *s unless it contains a router. */
@@ -684,13 +681,19 @@ router_parse_list_from_string(const char **s, smartlist_t *dest)
continue;
}
- router = router_parse_entry_from_string(*s, end);
+ router = router_parse_entry_from_string(*s, end,
+ saved_location != SAVED_IN_CACHE);
- *s = end;
if (!router) {
log_warn(LD_DIR, "Error reading router; skipping");
+ *s = end;
continue;
}
+ if (saved_location != SAVED_NOWHERE) {
+ router->cache_info.saved_location = saved_location;
+ router->cache_info.saved_offset = *s - start;
+ }
+ *s = end;
smartlist_add(dest, router);
}
@@ -700,9 +703,11 @@ router_parse_list_from_string(const char **s, smartlist_t *dest)
/** Helper function: reads a single router entry from *<b>s</b> ...
* *<b>end</b>. Mallocs a new router and returns it if all goes well, else
* returns NULL.
+ * DOCDOC cache_copy
*/
routerinfo_t *
-router_parse_entry_from_string(const char *s, const char *end)
+router_parse_entry_from_string(const char *s, const char *end,
+ int cache_copy)
{
routerinfo_t *router = NULL;
char signed_digest[128];
@@ -749,7 +754,9 @@ router_parse_entry_from_string(const char *s, const char *end)
}
router = tor_malloc_zero(sizeof(routerinfo_t));
- router->cache_info.signed_descriptor_body = tor_strndup(s, end-s);
+ router->routerlist_index = -1;
+ if (cache_copy)
+ router->cache_info.signed_descriptor_body = tor_strndup(s, end-s);
router->cache_info.signed_descriptor_len = end-s;
memcpy(router->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
@@ -871,6 +878,13 @@ router_parse_entry_from_string(const char *s, const char *end)
router->contact_info = tor_strdup(tok->args[0]);
}
+ if ((tok = find_first_by_keyword(tokens, K_EVENTDNS))) {
+ router->has_old_dnsworkers = tok->n_args && !strcmp(tok->args[0], "0");
+ } else if (router->platform) {
+ if (! tor_version_as_new_as(router->platform, "0.1.2.2-alpha"))
+ router->has_old_dnsworkers = 1;
+ }
+
exit_policy_tokens = find_all_exitpolicy(tokens);
SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t,
if (router_add_exit_policy(router,t)<0) {
@@ -1071,6 +1085,15 @@ _compare_routerstatus_entries(const void **_a, const void **_b)
return memcmp(a->identity_digest, b->identity_digest, DIGEST_LEN);
}
+static void
+_free_duplicate_routerstatus_entry(void *e)
+{
+ log_warn(LD_DIR,
+ "Network-status has two entries for the same router. "
+ "Dropping one.");
+ routerstatus_free(e);
+}
+
/** Given a versioned (v2 or later) network-status object in <b>s</b>, try to
* parse it and return the result. Return NULL on failure. Check the
* signature of the network status, but do not (yet) check the signing key for
@@ -1213,20 +1236,8 @@ networkstatus_parse_from_string(const char *s)
smartlist_add(ns->entries, rs);
}
smartlist_sort(ns->entries, _compare_routerstatus_entries);
-
- /* Kill duplicate entries. */
- for (i=0; i < smartlist_len(ns->entries)-1; ++i) {
- routerstatus_t *rs1 = smartlist_get(ns->entries, i);
- routerstatus_t *rs2 = smartlist_get(ns->entries, i+1);
- if (!memcmp(rs1->identity_digest,
- rs2->identity_digest, DIGEST_LEN)) {
- log_warn(LD_DIR,
- "Network-status has two entries for the same router. "
- "Dropping one.");
- smartlist_del_keeporder(ns->entries, i--);
- routerstatus_free(rs1);
- }
- }
+ smartlist_uniq(ns->entries, _compare_routerstatus_entries,
+ _free_duplicate_routerstatus_entry);
if (tokenize_string(s, NULL, tokens, NETSTATUS)) {
log_warn(LD_DIR, "Error tokenizing network-status footer.");
@@ -1799,7 +1810,7 @@ tor_version_parse(const char *s, tor_version_t *out)
{
char *eos=NULL, *cp=NULL;
/* Format is:
- * "Tor " ? NUM dot NUM dot NUM [ ( pre | rc | dot ) NUM [ -cvs ] ]
+ * "Tor " ? NUM dot NUM dot NUM [ ( pre | rc | dot ) NUM [ - tag ] ]
*/
tor_assert(s);
tor_assert(out);
@@ -1825,7 +1836,6 @@ tor_version_parse(const char *s, tor_version_t *out)
if (!*eos) {
out->status = VER_RELEASE;
out->patchlevel = 0;
- out->cvs = IS_NOT_CVS;
return 0;
}
cp = eos;
@@ -1849,15 +1859,10 @@ tor_version_parse(const char *s, tor_version_t *out)
if (!eos || eos==cp) return -1;
cp = eos;
- /* Get cvs status and status tag. */
+ /* Get status tag. */
if (*cp == '-' || *cp == '.')
++cp;
strlcpy(out->status_tag, cp, sizeof(out->status_tag));
- if (0==strcmp(cp, "cvs")) {
- out->cvs = IS_CVS;
- } else {
- out->cvs = IS_NOT_CVS;
- }
return 0;
}
@@ -1881,11 +1886,7 @@ tor_version_compare(tor_version_t *a, tor_version_t *b)
else if ((i = a->patchlevel - b->patchlevel))
return i;
- if (a->major > 0 || a->minor > 0) {
- return strcmp(a->status_tag, b->status_tag);
- } else {
- return (a->cvs - b->cvs);
- }
+ return strcmp(a->status_tag, b->status_tag);
}
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
@@ -1928,22 +1929,9 @@ _compare_tor_version_str_ptr(const void **_a, const void **_b)
void
sort_version_list(smartlist_t *versions, int remove_duplicates)
{
- int i;
-
smartlist_sort(versions, _compare_tor_version_str_ptr);
- if (!remove_duplicates)
- return;
-
- for (i = 1; i < smartlist_len(versions); ++i) {
- const void *a, *b;
- a = smartlist_get(versions, i-1);
- b = smartlist_get(versions, i);
- /* use version_cmp so we catch multiple representations of the same
- * version */
- if (_compare_tor_version_str_ptr(&a,&b) == 0) {
- tor_free(smartlist_get(versions, i));
- smartlist_del_keeporder(versions, i--);
- }
- }
+
+ if (remove_duplicates)
+ smartlist_uniq(versions, _compare_tor_version_str_ptr, NULL);
}
diff --git a/src/or/test.c b/src/or/test.c
index 7a413f525b..4f9d79ccf5 100644
--- a/src/or/test.c
+++ b/src/or/test.c
@@ -416,19 +416,6 @@ test_crypto(void)
test_eq(0, crypto_pk_cmp_keys(pk1, pk2));
tor_free(cp);
- /* Check DER encoding */
- i=crypto_pk_DER64_encode_public_key(pk1, &cp);
- test_assert(i>0);
- test_assert(cp);
- test_assert(!strchr(cp, ' '));
- test_assert(!strchr(cp, '\n'));
- test_eq(0, crypto_pk_cmp_keys(pk1, pk1));
- crypto_free_pk_env(pk2);
- pk2 = crypto_pk_DER64_decode_public_key(cp);
- test_assert(pk2);
- test_eq(0, crypto_pk_cmp_keys(pk1, pk2));
- tor_free(cp);
-
test_eq(128, crypto_pk_keysize(pk1));
test_eq(128, crypto_pk_keysize(pk2));
@@ -786,6 +773,16 @@ test_util(void)
test_streq("and", smartlist_bsearch(sl, " AND", _compare_without_first_ch));
test_eq_ptr(NULL, smartlist_bsearch(sl, " ANz", _compare_without_first_ch));
+ /* Test reverse() and pop_last() */
+ smartlist_reverse(sl);
+ cp = smartlist_join_strings(sl, ",", 0, NULL);
+ test_streq(cp,"the,router,onion,nickm,by,arma,and");
+ tor_free(cp);
+ cp = smartlist_pop_last(sl);
+ test_streq(cp, "and");
+ tor_free(cp);
+ test_eq(smartlist_len(sl), 6);
+
/* Test tor_strstrip() */
strcpy(buf, "Testing 1 2 3");
test_eq(0, tor_strstrip(buf, ",!"));
@@ -810,27 +807,28 @@ test_util(void)
/* Test parse_addr_port */
cp = NULL; u32 = 3; u16 = 3;
- test_assert(!parse_addr_port("1.2.3.4", &cp, &u32, &u16));
+ test_assert(!parse_addr_port(LOG_WARN, "1.2.3.4", &cp, &u32, &u16));
test_streq(cp, "1.2.3.4");
test_eq(u32, 0x01020304u);
test_eq(u16, 0);
tor_free(cp);
- test_assert(!parse_addr_port("4.3.2.1:99", &cp, &u32, &u16));
+ test_assert(!parse_addr_port(LOG_WARN, "4.3.2.1:99", &cp, &u32, &u16));
test_streq(cp, "4.3.2.1");
test_eq(u32, 0x04030201u);
test_eq(u16, 99);
tor_free(cp);
- test_assert(!parse_addr_port("nonexistent.address:4040", &cp, NULL, &u16));
+ test_assert(!parse_addr_port(LOG_WARN, "nonexistent.address:4040",
+ &cp, NULL, &u16));
test_streq(cp, "nonexistent.address");
test_eq(u16, 4040);
tor_free(cp);
- test_assert(!parse_addr_port("localhost:9999", &cp, &u32, &u16));
+ test_assert(!parse_addr_port(LOG_WARN, "localhost:9999", &cp, &u32, &u16));
test_streq(cp, "localhost");
test_eq(u32, 0x7f000001u);
test_eq(u16, 9999);
tor_free(cp);
u32 = 3;
- test_assert(!parse_addr_port("localhost", NULL, &u32, &u16));
+ test_assert(!parse_addr_port(LOG_WARN, "localhost", NULL, &u32, &u16));
test_eq(cp, NULL);
test_eq(u32, 0x7f000001u);
test_eq(u16, 0);
@@ -923,11 +921,73 @@ test_util(void)
smartlist_free(sl);
}
+static int
+_compare_strings_for_pqueue(const void *s1, const void *s2)
+{
+ return strcmp((const char*)s1, (const char*)s2);
+}
+
+static void
+test_pqueue(void)
+{
+ smartlist_t *sl;
+ int (*cmp)(const void *, const void*);
+#define OK() smartlist_pqueue_assert_ok(sl, cmp)
+
+ cmp = _compare_strings_for_pqueue;
+
+ sl = smartlist_create();
+ smartlist_pqueue_add(sl, cmp, (char*)"cows");
+ smartlist_pqueue_add(sl, cmp, (char*)"zebras");
+ smartlist_pqueue_add(sl, cmp, (char*)"fish");
+ smartlist_pqueue_add(sl, cmp, (char*)"frogs");
+ smartlist_pqueue_add(sl, cmp, (char*)"apples");
+ smartlist_pqueue_add(sl, cmp, (char*)"squid");
+ smartlist_pqueue_add(sl, cmp, (char*)"daschunds");
+ smartlist_pqueue_add(sl, cmp, (char*)"eggplants");
+ smartlist_pqueue_add(sl, cmp, (char*)"weissbier");
+ smartlist_pqueue_add(sl, cmp, (char*)"lobsters");
+ smartlist_pqueue_add(sl, cmp, (char*)"roquefort");
+
+ OK();
+
+ test_eq(smartlist_len(sl), 11);
+ test_streq(smartlist_get(sl, 0), "apples");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "apples");
+ test_eq(smartlist_len(sl), 10);
+ OK();
+ test_streq(smartlist_pqueue_pop(sl, cmp), "cows");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "daschunds");
+ smartlist_pqueue_add(sl, cmp, (char*)"chinchillas");
+ OK();
+ smartlist_pqueue_add(sl, cmp, (char*)"fireflies");
+ OK();
+ test_streq(smartlist_pqueue_pop(sl, cmp), "chinchillas");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "eggplants");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "fireflies");
+ OK();
+ test_streq(smartlist_pqueue_pop(sl, cmp), "fish");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "frogs");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "lobsters");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "roquefort");
+ OK();
+ test_eq(smartlist_len(sl), 3);
+ test_streq(smartlist_pqueue_pop(sl, cmp), "squid");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "weissbier");
+ test_streq(smartlist_pqueue_pop(sl, cmp), "zebras");
+ test_eq(smartlist_len(sl), 0);
+ OK();
+#undef OK
+ smartlist_free(sl);
+}
+
static void
test_gzip(void)
{
- char *buf1, *buf2=NULL, *buf3=NULL;
+ char *buf1, *buf2=NULL, *buf3=NULL, *cp1, *cp2;
+ const char *ccp2;
size_t len1, len2;
+ tor_zlib_state_t *state;
buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ");
test_eq(detect_compression_method(buf1, strlen(buf1)), 0);
@@ -993,6 +1053,35 @@ test_gzip(void)
ZLIB_METHOD, 1, LOG_INFO));
tor_assert(!buf3);
+ /* Now, try streaming compression. */
+ tor_free(buf1);
+ tor_free(buf2);
+ tor_free(buf3);
+ state = tor_zlib_new(1, ZLIB_METHOD);
+ tor_assert(state);
+ cp1 = buf1 = tor_malloc(1024);
+ len1 = 1024;
+ ccp2 = "ABCDEFGHIJABCDEFGHIJ";
+ len2 = 21;
+ test_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 0)
+ == TOR_ZLIB_OK);
+ test_eq(len2, 0); /* Make sure we compressed it all. */
+ test_assert(cp1 > buf1);
+
+ len2 = 0;
+ cp2 = cp1;
+ test_assert(tor_zlib_process(state, &cp1, &len1, &ccp2, &len2, 1)
+ == TOR_ZLIB_DONE);
+ test_eq(len2, 0);
+ test_assert(cp1 > cp2); /* Make sure we really added something. */
+
+ tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1,
+ ZLIB_METHOD, 1, LOG_WARN));
+ test_streq(buf3, "ABCDEFGHIJABCDEFGHIJ"); /*Make sure it compressed right.*/
+ tor_free(buf3);
+
+ tor_zlib_free(state);
+
tor_free(buf2);
tor_free(buf3);
tor_free(buf1);
@@ -1016,8 +1105,10 @@ test_strmap(void)
test_eq_ptr(strmap_get(map,"K1"), (void*)100);
test_eq_ptr(strmap_get(map,"K2"), (void*)101);
test_eq_ptr(strmap_get(map,"K-not-there"), NULL);
+ strmap_assert_ok(map);
v = strmap_remove(map,"K2");
+ strmap_assert_ok(map);
test_eq_ptr(v, (void*)101);
test_eq_ptr(strmap_get(map,"K2"), NULL);
test_eq_ptr(strmap_remove(map,"K2"), NULL);
@@ -1025,8 +1116,10 @@ test_strmap(void)
strmap_set(map, "K2", (void*)101);
strmap_set(map, "K3", (void*)102);
strmap_set(map, "K4", (void*)103);
+ strmap_assert_ok(map);
strmap_set(map, "K5", (void*)104);
strmap_set(map, "K6", (void*)105);
+ strmap_assert_ok(map);
#if 0
iter = strmap_iter_init(map);
@@ -1053,6 +1146,7 @@ test_strmap(void)
test_eq_ptr(strmap_get(map, "K5"), (void*)10816);
#endif
+ strmap_assert_ok(map);
/* Clean up after ourselves. */
strmap_free(map, NULL);
@@ -1060,9 +1154,11 @@ test_strmap(void)
map = strmap_new();
strmap_set_lc(map,"Ab.C", (void*)1);
test_eq_ptr(strmap_get(map,"ab.c"), (void*)1);
+ strmap_assert_ok(map);
test_eq_ptr(strmap_get_lc(map,"AB.C"), (void*)1);
test_eq_ptr(strmap_get(map,"AB.C"), NULL);
test_eq_ptr(strmap_remove_lc(map,"aB.C"), (void*)1);
+ strmap_assert_ok(map);
test_eq_ptr(strmap_get_lc(map,"AB.C"), NULL);
strmap_free(map,NULL);
}
@@ -1145,6 +1241,8 @@ test_onion_handshake(void)
crypto_free_pk_env(pk);
}
+extern smartlist_t *fingerprint_list;
+
static void
test_dir_format(void)
{
@@ -1259,6 +1357,11 @@ test_dir_format(void)
strcat(buf2, pk1_str);
strcat(buf2, "signing-key\n");
strcat(buf2, pk2_str);
+#ifdef USE_EVENTDNS
+ strcat(buf2, "opt eventdns 1\n");
+#else
+ strcat(buf2, "opt eventdns 0\n");
+#endif
strcat(buf2, bw_lines);
strcat(buf2, "router-signature\n");
buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
@@ -1269,7 +1372,7 @@ test_dir_format(void)
test_assert(router_dump_router_to_string(buf, 2048, &r1, pk2)>0);
cp = buf;
- rp1 = router_parse_entry_from_string((const char*)cp,NULL);
+ rp1 = router_parse_entry_from_string((const char*)cp,NULL,1);
test_assert(rp1);
test_streq(rp1->address, r1.address);
test_eq(rp1->or_port, r1.or_port);
@@ -1292,7 +1395,7 @@ test_dir_format(void)
test_streq(buf, buf2);
cp = buf;
- rp2 = router_parse_entry_from_string(&cp);
+ rp2 = router_parse_entry_from_string(&cp,1);
test_assert(rp2);
test_streq(rp2->address, r2.address);
test_eq(rp2->or_port, r2.or_port);
@@ -1313,7 +1416,6 @@ test_dir_format(void)
/* Okay, now for the directories. */
{
- extern smartlist_t *fingerprint_list;
fingerprint_list = smartlist_create();
crypto_pk_get_fingerprint(pk2, buf, 1);
add_fingerprint_to_dir("Magri", buf, fingerprint_list);
@@ -1333,7 +1435,7 @@ test_dir_format(void)
test_assert(router_dump_router_to_string(buf, 2048, &r2, pk1)>0);
test_eq(dirserv_add_descriptor(buf,&m), 2);
get_options()->Nickname = tor_strdup("DirServer");
- test_assert(!dirserv_dump_directory_to_string(&cp,pk3));
+ test_assert(!dirserv_dump_directory_to_string(&cp,pk3, 0));
crypto_pk_get_digest(pk3, d);
test_assert(!router_parse_directory(cp));
test_eq(2, smartlist_len(dir1->routers));
@@ -1358,35 +1460,30 @@ test_dir_format(void)
test_eq(4, ver1.micro);
test_eq(VER_PRE, ver1.status);
test_eq(2, ver1.patchlevel);
- test_eq(IS_CVS, ver1.cvs);
test_eq(0, tor_version_parse("0.3.4rc1", &ver1));
test_eq(0, ver1.major);
test_eq(3, ver1.minor);
test_eq(4, ver1.micro);
test_eq(VER_RC, ver1.status);
test_eq(1, ver1.patchlevel);
- test_eq(IS_NOT_CVS, ver1.cvs);
test_eq(0, tor_version_parse("1.3.4", &ver1));
test_eq(1, ver1.major);
test_eq(3, ver1.minor);
test_eq(4, ver1.micro);
test_eq(VER_RELEASE, ver1.status);
test_eq(0, ver1.patchlevel);
- test_eq(IS_NOT_CVS, ver1.cvs);
test_eq(0, tor_version_parse("1.3.4.999", &ver1));
test_eq(1, ver1.major);
test_eq(3, ver1.minor);
test_eq(4, ver1.micro);
test_eq(VER_RELEASE, ver1.status);
test_eq(999, ver1.patchlevel);
- test_eq(IS_NOT_CVS, ver1.cvs);
test_eq(0, tor_version_parse("0.1.2.4-alpha", &ver1));
test_eq(0, ver1.major);
test_eq(1, ver1.minor);
test_eq(2, ver1.micro);
test_eq(4, ver1.patchlevel);
test_eq(VER_RELEASE, ver1.status);
- test_eq(IS_NOT_CVS, ver1.cvs);
test_streq("alpha", ver1.status_tag);
test_eq(0, tor_version_parse("0.1.2.4", &ver1));
test_eq(0, ver1.major);
@@ -1394,7 +1491,6 @@ test_dir_format(void)
test_eq(2, ver1.micro);
test_eq(4, ver1.patchlevel);
test_eq(VER_RELEASE, ver1.status);
- test_eq(IS_NOT_CVS, ver1.cvs);
test_streq("", ver1.status_tag);
#define test_eq_vs(vs1, vs2) test_eq_type(version_status_t, "%d", (vs1), (vs2))
@@ -1615,6 +1711,8 @@ main(int c, char**v)
{
or_options_t *options = options_new();
char *errmsg = NULL;
+ (void) c;
+ (void) v;
options->command = CMD_RUN_UNITTESTS;
network_init();
setup_directory();
@@ -1650,6 +1748,7 @@ main(int c, char**v)
test_util();
test_strmap();
test_control_formats();
+ test_pqueue();
puts("\n========================= Onion Skins =====================");
test_onion();
test_onion_handshake();
diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c
index 052526698e..6e1b59ecfd 100644
--- a/src/tools/tor-resolve.c
+++ b/src/tools/tor-resolve.c
@@ -217,7 +217,7 @@ main(int argc, char **argv)
sockshost = 0x7f000001u; /* localhost */
socksport = 9050; /* 9050 */
} else if (n_args == 2) {
- if (parse_addr_port(arg[1], NULL, &sockshost, &socksport)<0) {
+ if (parse_addr_port(LOG_WARN, arg[1], NULL, &sockshost, &socksport)<0) {
fprintf(stderr, "Couldn't parse/resolve address %s", arg[1]);
return 1;
}
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 629d5c82a8..0836d59605 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -221,5 +221,5 @@
#define HAVE_EVENT_H
/* Version number of package */
-#define VERSION "0.1.1.23"
+#define VERSION "0.1.2.1-alpha-dev"
diff --git a/tor.spec.in b/tor.spec.in
index 9482b0529f..405088c18e 100644
--- a/tor.spec.in
+++ b/tor.spec.in
@@ -84,9 +84,9 @@
# Using the build date ensures that every build really does get
# a different release number. We use this trick for CVS versions.
# For release versions, we don't want or need it.
-%define is_cvs_version %(echo %{native_version} | grep 'cvs' > /dev/null && echo 1 || echo 0)
+%define is_dev_version %(echo %{native_version} | grep 'dev' > /dev/null && echo 1 || echo 0)
-%if %{is_cvs_version}
+%if %{is_dev_version}
%define blddate %(date -u +"%Y%m%d%H%M")
%define release %{pkgspec}.%{specver}.%{ostag}.%{blddate}
%else
@@ -122,7 +122,7 @@ Group: System Environment/Daemons
License: BSD-like
Vendor: R. Dingledine <arma@seul.org>
-Packager: Nick Mathewson <nickm@seul.org>
+Packager: Andrew Lewman <phobos@interloper.org>
%if %{is_suse}
Requires: openssl >= 0.9.6