diff options
Diffstat (limited to 'src/test')
103 files changed, 5235 insertions, 452 deletions
diff --git a/src/test/conf_examples/badnick_1/error b/src/test/conf_examples/badnick_1/error new file mode 100644 index 0000000000..3e92ddc832 --- /dev/null +++ b/src/test/conf_examples/badnick_1/error @@ -0,0 +1 @@ +nicknames must be between 1 and 19 characters inclusive diff --git a/src/test/conf_examples/badnick_1/torrc b/src/test/conf_examples/badnick_1/torrc new file mode 100644 index 0000000000..41ee4894f1 --- /dev/null +++ b/src/test/conf_examples/badnick_1/torrc @@ -0,0 +1,2 @@ +# This nickname is too long; we won't accept it. +Nickname TooManyCharactersInThisNickname diff --git a/src/test/conf_examples/badnick_2/error b/src/test/conf_examples/badnick_2/error new file mode 100644 index 0000000000..ceac99f012 --- /dev/null +++ b/src/test/conf_examples/badnick_2/error @@ -0,0 +1 @@ +must contain only the characters \[a-zA-Z0-9\] diff --git a/src/test/conf_examples/badnick_2/torrc b/src/test/conf_examples/badnick_2/torrc new file mode 100644 index 0000000000..07acc61698 --- /dev/null +++ b/src/test/conf_examples/badnick_2/torrc @@ -0,0 +1,2 @@ +# this nickname has spaces in it and won't work. +Nickname has a space diff --git a/src/test/conf_examples/contactinfo_notutf8/error b/src/test/conf_examples/contactinfo_notutf8/error new file mode 100644 index 0000000000..6d165152ce --- /dev/null +++ b/src/test/conf_examples/contactinfo_notutf8/error @@ -0,0 +1 @@ +ContactInfo config option must be UTF-8 diff --git a/src/test/conf_examples/contactinfo_notutf8/torrc b/src/test/conf_examples/contactinfo_notutf8/torrc new file mode 100644 index 0000000000..2ee4d093c5 --- /dev/null +++ b/src/test/conf_examples/contactinfo_notutf8/torrc @@ -0,0 +1 @@ +ContactInfo ÄëÄëÄë@example.com diff --git a/src/test/conf_examples/example_1/expected b/src/test/conf_examples/example_1/expected new file mode 100644 index 0000000000..9d6688a565 --- /dev/null +++ b/src/test/conf_examples/example_1/expected @@ -0,0 +1,2 @@ +ContactInfo tor_tellini@example.com +SocksPort 80 diff --git a/src/test/conf_examples/example_1/torrc b/src/test/conf_examples/example_1/torrc new file mode 100644 index 0000000000..bff7fa0aa2 --- /dev/null +++ b/src/test/conf_examples/example_1/torrc @@ -0,0 +1,5 @@ + +# Here is a simple example torrc. + SocksPort 80 + +ContactInfo "tor_tellini@example.com" diff --git a/src/test/conf_examples/example_2/error b/src/test/conf_examples/example_2/error new file mode 100644 index 0000000000..ce18b68db4 --- /dev/null +++ b/src/test/conf_examples/example_2/error @@ -0,0 +1 @@ +Unknown option 'JumpingJellyjars' diff --git a/src/test/conf_examples/example_2/torrc b/src/test/conf_examples/example_2/torrc new file mode 100644 index 0000000000..8ec8133b24 --- /dev/null +++ b/src/test/conf_examples/example_2/torrc @@ -0,0 +1 @@ +JumpingJellyjars 1 diff --git a/src/test/conf_examples/example_3/cmdline b/src/test/conf_examples/example_3/cmdline new file mode 100644 index 0000000000..5b2fadcebb --- /dev/null +++ b/src/test/conf_examples/example_3/cmdline @@ -0,0 +1 @@ +--socksport 99 diff --git a/src/test/conf_examples/example_3/expected b/src/test/conf_examples/example_3/expected new file mode 100644 index 0000000000..867fb8bcc8 --- /dev/null +++ b/src/test/conf_examples/example_3/expected @@ -0,0 +1 @@ +SocksPort 99 diff --git a/src/test/conf_examples/example_3/torrc b/src/test/conf_examples/example_3/torrc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/test/conf_examples/example_3/torrc diff --git a/src/test/conf_examples/include_1/expected b/src/test/conf_examples/include_1/expected new file mode 100644 index 0000000000..4bbf52ce9f --- /dev/null +++ b/src/test/conf_examples/include_1/expected @@ -0,0 +1,3 @@ +ContactInfo includefile@example.com +Nickname nested +ORPort 8008 diff --git a/src/test/conf_examples/include_1/included.inc b/src/test/conf_examples/include_1/included.inc new file mode 100644 index 0000000000..8d1834345d --- /dev/null +++ b/src/test/conf_examples/include_1/included.inc @@ -0,0 +1,4 @@ + +ContactInfo includefile@example.com + +%include "nested.inc"
\ No newline at end of file diff --git a/src/test/conf_examples/include_1/nested.inc b/src/test/conf_examples/include_1/nested.inc new file mode 100644 index 0000000000..789b044a2b --- /dev/null +++ b/src/test/conf_examples/include_1/nested.inc @@ -0,0 +1,2 @@ + +Nickname nested
\ No newline at end of file diff --git a/src/test/conf_examples/include_1/torrc b/src/test/conf_examples/include_1/torrc new file mode 100644 index 0000000000..2ed4074f6e --- /dev/null +++ b/src/test/conf_examples/include_1/torrc @@ -0,0 +1,4 @@ + +%include "included.inc" + +ORPort 8008 diff --git a/src/test/conf_examples/include_bug_31408/expected b/src/test/conf_examples/include_bug_31408/expected new file mode 100644 index 0000000000..2e822f1a78 --- /dev/null +++ b/src/test/conf_examples/include_bug_31408/expected @@ -0,0 +1,2 @@ +Nickname test31408 +ORPort 31408 diff --git a/src/test/conf_examples/include_bug_31408/included/01_nickname.inc b/src/test/conf_examples/include_bug_31408/included/01_nickname.inc new file mode 100644 index 0000000000..508dd89a35 --- /dev/null +++ b/src/test/conf_examples/include_bug_31408/included/01_nickname.inc @@ -0,0 +1 @@ +Nickname test31408 diff --git a/src/test/conf_examples/include_bug_31408/included/02_no_configs.inc b/src/test/conf_examples/include_bug_31408/included/02_no_configs.inc new file mode 100644 index 0000000000..140e927f19 --- /dev/null +++ b/src/test/conf_examples/include_bug_31408/included/02_no_configs.inc @@ -0,0 +1,3 @@ +# Bug 31048 is triggered when the last file in a config directory: +# * contains no configuration options, +# * but is non-empty: that is, it contains comments or whitespace. diff --git a/src/test/conf_examples/include_bug_31408/torrc b/src/test/conf_examples/include_bug_31408/torrc new file mode 100644 index 0000000000..a42685e93c --- /dev/null +++ b/src/test/conf_examples/include_bug_31408/torrc @@ -0,0 +1,2 @@ +%include "included" +ORPort 31408 diff --git a/src/test/conf_examples/large_1/expected b/src/test/conf_examples/large_1/expected new file mode 100644 index 0000000000..5866f5823e --- /dev/null +++ b/src/test/conf_examples/large_1/expected @@ -0,0 +1,159 @@ +AccountingMax 10737418240 +AccountingRule sum +AccountingStart day 05:15 +Address 128.66.8.8 +AllowNonRFC953Hostnames 1 +AndroidIdentityTag droidy +AutomapHostsOnResolve 1 +AutomapHostsSuffixes .onions +AvoidDiskWrites 1 +BandwidthBurst 2147483647 +BandwidthRate 1610612736 +Bridge 128.66.1.10:80 +CacheDirectory /this-is-a-cache +CellStatistics 1 +CircuitBuildTimeout 200 +CircuitsAvailableTimeout 10 +CircuitStreamTimeout 20 +ClientAutoIPv6ORPort 1 +ClientOnly 1 +ClientPreferIPv6DirPort 1 +ClientPreferIPv6ORPort 1 +ClientRejectInternalAddresses 0 +ClientUseIPv4 0 +ClientUseIPv6 1 +ConnDirectionStatistics 1 +ConnectionPadding 1 +ConnLimit 64 +ConsensusParams wombat=7 +ConstrainedSockets 1 +ConstrainedSockSize 10240 +ContactInfo long_config@example.com +ControlPortFileGroupReadable 1 +ControlPort 9058 +CookieAuthentication 1 +CookieAuthFile /control/cookie +CookieAuthFileGroupReadable 1 +CountPrivateBandwidth 1 +DataDirectory /data/dir +DirAllowPrivateAddresses 1 +DirPolicy reject 128.66.1.1/32, accept *:* +DirPortFrontPage /dirport/frontpage +DirPort 99 +DirReqStatistics 0 +DisableDebuggerAttachment 0 +DisableNetwork 1 +DisableOOSCheck 0 +DNSPort 53535 +DormantCanceledByStartup 1 +DormantClientTimeout 1260 +DormantOnFirstStartup 1 +DormantTimeoutDisabledByIdleStreams 0 +DoSCircuitCreationBurst 1000 +DoSCircuitCreationDefenseTimePeriod 300 +DoSCircuitCreationDefenseType 2 +DoSCircuitCreationEnabled 1 +DoSCircuitCreationMinConnections 10 +DoSCircuitCreationRate 100 +DoSConnectionDefenseType 2 +DoSConnectionEnabled 1 +DoSConnectionMaxConcurrentCount 6 +DoSRefuseSingleHopClientRendezvous 0 +DownloadExtraInfo 1 +EnforceDistinctSubnets 0 +EntryNodes potrzebie,triffid,cromulent +EntryStatistics 1 +ExcludeExitNodes blaznort,kriffid,zeppelin +ExcludeNodes 128.66.7.6 +ExitNodes 128.66.7.7,128.66.128.0/17,exitexit +ExitPolicy accept *:80,reject *:* +ExitPolicyRejectLocalInterfaces 1 +ExitPolicyRejectPrivate 0 +ExitPortStatistics 1 +ExitRelay 1 +ExtendAllowPrivateAddresses 1 +ExtendByEd25519ID 1 +ExtORPortCookieAuthFile /foobar +ExtORPort 99 +FascistFirewall 1 +FetchDirInfoEarly 1 +FetchDirInfoExtraEarly 1 +FetchUselessDescriptors 1 +FirewallPorts 80,443,999 +GeoIPExcludeUnknown 1 +GeoIPFile /geoip +GuardfractionFile /gff +GuardLifetime 691200 +HeartbeatPeriod 2700 +IPv6Exit 1 +KeepalivePeriod 540 +KeyDirectory /keyz +KISTSchedRunInterval 1 +Log notice file /logfile +Log info file /logfile-verbose +LogTimeGranularity 60000 +LongLivedPorts 9090 +MainloopStats 1 +MapAddress www.example.com:10.0.0.6 +MaxAdvertisedBandwidth 100 +MaxCircuitDirtiness 3600 +MaxClientCircuitsPending 127 +MaxConsensusAgeForDiffs 2629728 +MaxMemInQueues 314572800 +MaxOnionQueueDelay 60000 +MaxUnparseableDescSizeToLog 1048576 +MiddleNodes grommit,truffle,parcheesi +MyFamily $ffffffffffffffffffffffffffffffffffffffff +NewCircuitPeriod 7200 +Nickname nickname +NodeFamily $ffffffffffffffffffffffffffffffffffffffff,$dddddddddddddddddddddddddddddddddddddddd +NumCPUs 3 +NumDirectoryGuards 4 +NumEntryGuards 5 +NumPrimaryGuards 8 +OfflineMasterKey 1 +OptimisticData 1 +ORPort 2222 +OutboundBindAddress 10.0.0.7 +OutboundBindAddressExit 10.0.0.8 +OutboundBindAddressOR 10.0.0.9 +PerConnBWBurst 10485760 +PerConnBWRate 102400 +PidFile /piddy +ProtocolWarnings 1 +PublishHidServDescriptors 0 +PublishServerDescriptor 0 +ReachableAddresses 0.0.0.0, *:* +ReachableDirAddresses 128.0.0.0/1 +ReachableORAddresses 128.0.0.0/8 +RejectPlaintextPorts 23 +RelayBandwidthBurst 10000 +RelayBandwidthRate 1000 +RendPostPeriod 600 +RephistTrackTime 600 +SafeLogging 0 +Schedulers Vanilla,KISTLite,Kist +ShutdownWaitLength 10 +SigningKeyLifetime 4838400 +Socks5Proxy 128.66.99.99:99 +Socks5ProxyPassword flynn +Socks5ProxyUsername spaceparanoids +SocksPolicy accept 127.0.0.0/24, reject *:* +SocksPort 9099 +SocksTimeout 600 +SSLKeyLifetime 86400 +StrictNodes 1 +SyslogIdentityTag tortor +TestSocks 1 +TokenBucketRefillInterval 1000 +TrackHostExits www.example.com +TrackHostExitsExpire 3600 +TruncateLogFile 1 +UnixSocksGroupWritable 1 +UpdateBridgesFromAuthority 1 +UseDefaultFallbackDirs 0 +UseGuardFraction 1 +UseMicrodescriptors 0 +VirtualAddrNetworkIPv4 18.66.0.0/16 +VirtualAddrNetworkIPv6 [ff00::]/16 +WarnPlaintextPorts 7,11,23,1001 diff --git a/src/test/conf_examples/large_1/torrc b/src/test/conf_examples/large_1/torrc new file mode 100644 index 0000000000..e99acd9fb7 --- /dev/null +++ b/src/test/conf_examples/large_1/torrc @@ -0,0 +1,167 @@ +AccountingMax 10 GB +AccountingRule sum +AccountingStart day 05:15 +Address 128.66.8.8 +AllowNonRFC953Hostnames 1 +AndroidIdentityTag droidy +AutomapHostsOnResolve 1 +AutomapHostsSuffixes .onions +AvoidDiskWrites 1 +BandwidthBurst 2 GB +BandwidthRate 1.5 GB +Bridge 128.66.1.10:80 +CacheDirectory /this-is-a-cache +CellStatistics 1 +CircuitBuildTimeout 200 +CircuitPadding 1 +CircuitsAvailableTimeout 10 +CircuitStreamTimeout 20 +ClientAutoIPv6ORPort 1 +ClientOnly 1 +ClientPreferIPv6DirPort 1 +ClientPreferIPv6ORPort 1 +ClientRejectInternalAddresses 0 +ClientUseIPv4 0 +ClientUseIPv6 1 +ConnDirectionStatistics 1 +ConnectionPadding 1 +ConnLimit 64 +ConsensusParams wombat=7 +ConstrainedSockets 1 +ConstrainedSockSize 10240 +ContactInfo long_config@example.com +ControlPortFileGroupReadable 1 +ControlPort 9058 +CookieAuthentication 1 +CookieAuthFile /control/cookie +CookieAuthFileGroupReadable 1 +CountPrivateBandwidth 1 +DataDirectory /data/dir +DirAllowPrivateAddresses 1 +DirPolicy reject 128.66.1.1/32, accept *:* +DirReqStatistics 0 +DirPort 99 +DirPortFrontPage /dirport/frontpage +DisableDebuggerAttachment 0 +DisableNetwork 1 +DisableOOSCheck 0 +DNSPort 53535 +DormantCanceledByStartup 1 +DormantClientTimeout 21 minutes +DormantOnFirstStartup 1 +DormantTimeoutDisabledByIdleStreams 0 +DoSCircuitCreationBurst 1000 +DoSCircuitCreationDefenseTimePeriod 5 minutes +DoSCircuitCreationDefenseType 2 +DoSCircuitCreationEnabled 1 +DoSCircuitCreationMinConnections 10 +DoSCircuitCreationRate 100 +DoSConnectionDefenseType 2 +DoSConnectionEnabled 1 +DoSConnectionMaxConcurrentCount 6 +DoSRefuseSingleHopClientRendezvous 0 +DownloadExtraInfo 1 +EnforceDistinctSubnets 0 +EntryNodes potrzebie,triffid,cromulent +EntryStatistics 1 +ExcludeExitNodes blaznort,kriffid,zeppelin +ExcludeNodes 128.66.7.6 +ExitNodes 128.66.7.7,128.66.128.0/17,exitexit +ExitPolicy accept *:80,reject *:* +ExitPolicyRejectLocalInterfaces 1 +ExitPolicyRejectPrivate 0 +ExitPortStatistics 1 +ExitRelay 1 +ExtendAllowPrivateAddresses 1 +ExtendByEd25519ID 1 +ExtORPort 99 +ExtORPortCookieAuthFile /foobar +ExtraInfoStatistics 1 +FascistFirewall 1 +FetchDirInfoEarly 1 +FetchDirInfoExtraEarly 1 +FetchHidServDescriptors 1 +FetchServerDescriptors 1 +FetchUselessDescriptors 1 +FirewallPorts 80,443,999 +GeoIPExcludeUnknown 1 +GeoIPFile /geoip +GuardfractionFile /gff +GuardLifetime 8 days +HeartbeatPeriod 45 minutes +IPv6Exit 1 +KeepalivePeriod 9 minutes +KeyDirectory /keyz +KISTSchedRunInterval 1 msec +LearnCircuitBuildTimeout 1 +Log notice file /logfile +Log info file /logfile-verbose +LogTimeGranularity 1 minute +LongLivedPorts 9090 +MainloopStats 1 +MapAddress www.example.com:10.0.0.6 +MaxAdvertisedBandwidth 100 +MaxCircuitDirtiness 1 hour +MaxClientCircuitsPending 127 +MaxConsensusAgeForDiffs 1 month +MaxMemInQueues 300 MB +MaxOnionQueueDelay 60 seconds +MaxUnparseableDescSizeToLog 1 MB +MiddleNodes grommit, truffle, parcheesi +MyFamily $ffffffffffffffffffffffffffffffffffffffff +NewCircuitPeriod 2 hours +Nickname nickname +NodeFamily $ffffffffffffffffffffffffffffffffffffffff,$dddddddddddddddddddddddddddddddddddddddd +NumCPUs 3 +NumDirectoryGuards 4 +NumEntryGuards 5 +NumPrimaryGuards 8 +OfflineMasterKey 1 +OptimisticData 1 +ORPort 2222 +OutboundBindAddress 10.0.0.7 +OutboundBindAddressExit 10.0.0.8 +OutboundBindAddressOR 10.0.0.9 +PaddingStatistics 1 +PerConnBWBurst 10 MB +PerConnBWRate 100 kb +PidFile /piddy +ProtocolWarnings 1 +PublishHidServDescriptors 0 +PublishServerDescriptor 0 +ReachableAddresses 0.0.0.0, *:* +ReachableDirAddresses 128.0.0.0/1 +ReachableORAddresses 128.0.0.0/8 +RejectPlaintextPorts 23 +RelayBandwidthBurst 10000 +RelayBandwidthRate 1000 +RendPostPeriod 10 minutes +RephistTrackTime 10 minutes +SafeLogging 0 +SafeSocks 0 +Schedulers Vanilla,KISTLite,Kist +ShutdownWaitLength 10 seconds +SigningKeyLifetime 8 weeks +Socks5Proxy 128.66.99.99:99 +Socks5ProxyPassword flynn +Socks5ProxyUsername spaceparanoids +SocksPolicy accept 127.0.0.0/24, reject *:* +SocksPort 9099 +SocksTimeout 10 minutes +SSLKeyLifetime 1 day +StrictNodes 1 +SyslogIdentityTag tortor +TestSocks 1 +TokenBucketRefillInterval 1 second +TrackHostExits www.example.com +TrackHostExitsExpire 1 hour +TruncateLogFile 1 +UnixSocksGroupWritable 1 +UpdateBridgesFromAuthority 1 +UseDefaultFallbackDirs 0 +UseEntryGuards 1 +UseGuardFraction 1 +UseMicrodescriptors 0 +VirtualAddrNetworkIPv4 18.66.0.0/16 +VirtualAddrNetworkIPv6 [ff00::]/16 +WarnPlaintextPorts 7,11,23,1001 diff --git a/src/test/conf_examples/obsolete_1/expected b/src/test/conf_examples/obsolete_1/expected new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/test/conf_examples/obsolete_1/expected diff --git a/src/test/conf_examples/obsolete_1/torrc b/src/test/conf_examples/obsolete_1/torrc new file mode 100644 index 0000000000..3cd9a6d777 --- /dev/null +++ b/src/test/conf_examples/obsolete_1/torrc @@ -0,0 +1,68 @@ +# These options are obsolete as of 0.4.2 +AllowDotExit +AllowInvalidNodes +AllowSingleHopCircuits +AllowSingleHopExits +AlternateHSAuthority +AuthDirBadDir +AuthDirBadDirCCs +AuthDirRejectUnlisted +AuthDirListBadDirs +AuthDirMaxServersPerAuthAddr +CircuitIdleTimeout +ControlListenAddress +DirListenAddress +DisableIOCP +DisableV2DirectoryInfo_ +DynamicDHGroups +DNSListenAddress +TestingEnableTbEmptyEvent +ExcludeSingleHopRelays +FallbackNetworkstatusFile +FastFirstHopPK +FetchV2Networkstatus +Group +HidServDirectoryV2 +CloseHSClientCircuitsImmediatelyOnTimeout +CloseHSServiceRendCircuitsImmediatelyOnTimeout +MaxOnionsPending +NamingAuthoritativeDirectory +NATDListenAddress +PredictedPortsRelevanceTime +WarnUnsafeSocks +ORListenAddress +PathBiasDisableRate +PathBiasScaleFactor +PathBiasMultFactor +PathBiasUseCloseCounts +PortForwarding +PortForwardingHelper +PreferTunneledDirConns +RecommendedPackages +RunTesting +SchedulerLowWaterMark__ +SchedulerHighWaterMark__ +SchedulerMaxFlushCells__ +SocksListenAddress +StrictEntryNodes +StrictExitNodes +Support022HiddenServices +Tor2webMode +Tor2webRendezvousPoints +TLSECGroup +TransListenAddress +TunnelDirConns +UseEntryGuardsAsDirGuards +UseNTorHandshake +UserspaceIOCPBuffers +V1AuthoritativeDirectory +V2AuthoritativeDirectory +VoteOnHidServDirectoriesV2 +UseFilteringSSLBufferevents +__UseFilteringSSLBufferevents +TestingConsensusMaxDownloadTries +ClientBootstrapConsensusMaxDownloadTries +ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries +TestingDescriptorMaxDownloadTries +TestingMicrodescMaxDownloadTries +TestingCertMaxDownloadTries diff --git a/src/test/conf_examples/obsolete_2/expected b/src/test/conf_examples/obsolete_2/expected new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/test/conf_examples/obsolete_2/expected diff --git a/src/test/conf_examples/obsolete_2/torrc b/src/test/conf_examples/obsolete_2/torrc new file mode 100644 index 0000000000..4f78d47625 --- /dev/null +++ b/src/test/conf_examples/obsolete_2/torrc @@ -0,0 +1,2 @@ +# This option has been obsolete for some time +AllowDotExit diff --git a/src/test/conf_examples/ops_1/cmdline b/src/test/conf_examples/ops_1/cmdline new file mode 100644 index 0000000000..2bb9bfa132 --- /dev/null +++ b/src/test/conf_examples/ops_1/cmdline @@ -0,0 +1 @@ +ORPort 1000 diff --git a/src/test/conf_examples/ops_1/expected b/src/test/conf_examples/ops_1/expected new file mode 100644 index 0000000000..84be6a70e2 --- /dev/null +++ b/src/test/conf_examples/ops_1/expected @@ -0,0 +1,2 @@ +Nickname Unnamed +ORPort 1000 diff --git a/src/test/conf_examples/ops_1/torrc b/src/test/conf_examples/ops_1/torrc new file mode 100644 index 0000000000..daf8ae60fe --- /dev/null +++ b/src/test/conf_examples/ops_1/torrc @@ -0,0 +1,3 @@ +# We'll replace this option on the command line. + +ORPort 9999 diff --git a/src/test/conf_examples/ops_2/cmdline b/src/test/conf_examples/ops_2/cmdline new file mode 100644 index 0000000000..fdd48a045c --- /dev/null +++ b/src/test/conf_examples/ops_2/cmdline @@ -0,0 +1 @@ +/ORPort diff --git a/src/test/conf_examples/ops_2/expected b/src/test/conf_examples/ops_2/expected new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/test/conf_examples/ops_2/expected diff --git a/src/test/conf_examples/ops_2/torrc b/src/test/conf_examples/ops_2/torrc new file mode 100644 index 0000000000..21fcc93f9a --- /dev/null +++ b/src/test/conf_examples/ops_2/torrc @@ -0,0 +1,3 @@ +# We'll remove this option on the command line, and not replace it. + +ORPort 9999 diff --git a/src/test/conf_examples/ops_3/cmdline b/src/test/conf_examples/ops_3/cmdline new file mode 100644 index 0000000000..e4965d26f8 --- /dev/null +++ b/src/test/conf_examples/ops_3/cmdline @@ -0,0 +1 @@ ++ORPort 1000 diff --git a/src/test/conf_examples/ops_3/expected b/src/test/conf_examples/ops_3/expected new file mode 100644 index 0000000000..569d26b577 --- /dev/null +++ b/src/test/conf_examples/ops_3/expected @@ -0,0 +1,3 @@ +Nickname Unnamed +ORPort 9999 +ORPort 1000 diff --git a/src/test/conf_examples/ops_3/torrc b/src/test/conf_examples/ops_3/torrc new file mode 100644 index 0000000000..14adf87d7f --- /dev/null +++ b/src/test/conf_examples/ops_3/torrc @@ -0,0 +1,3 @@ +# We will extend this option on the command line + +ORPort 9999 diff --git a/src/test/conf_examples/ops_4/expected b/src/test/conf_examples/ops_4/expected new file mode 100644 index 0000000000..bf52f6a330 --- /dev/null +++ b/src/test/conf_examples/ops_4/expected @@ -0,0 +1,2 @@ +Nickname Unnamed +ORPort 9099 diff --git a/src/test/conf_examples/ops_4/torrc b/src/test/conf_examples/ops_4/torrc new file mode 100644 index 0000000000..dcec2aa95d --- /dev/null +++ b/src/test/conf_examples/ops_4/torrc @@ -0,0 +1,3 @@ +# This value is unadorned, so replaces the one from defaults.torrc. + +ORPort 9099 diff --git a/src/test/conf_examples/ops_4/torrc.defaults b/src/test/conf_examples/ops_4/torrc.defaults new file mode 100644 index 0000000000..04cd0393c6 --- /dev/null +++ b/src/test/conf_examples/ops_4/torrc.defaults @@ -0,0 +1 @@ +ORPort 9000 diff --git a/src/test/conf_examples/ops_5/expected b/src/test/conf_examples/ops_5/expected new file mode 100644 index 0000000000..288721da53 --- /dev/null +++ b/src/test/conf_examples/ops_5/expected @@ -0,0 +1,3 @@ +Nickname Unnamed +ORPort 9000 +ORPort 9099 diff --git a/src/test/conf_examples/ops_5/torrc b/src/test/conf_examples/ops_5/torrc new file mode 100644 index 0000000000..3284fc1c55 --- /dev/null +++ b/src/test/conf_examples/ops_5/torrc @@ -0,0 +1,3 @@ +# This value has a plus, and so extends the one from defaults.torrc. + ++ORPort 9099 diff --git a/src/test/conf_examples/ops_5/torrc.defaults b/src/test/conf_examples/ops_5/torrc.defaults new file mode 100644 index 0000000000..04cd0393c6 --- /dev/null +++ b/src/test/conf_examples/ops_5/torrc.defaults @@ -0,0 +1 @@ +ORPort 9000 diff --git a/src/test/conf_examples/ops_6/expected b/src/test/conf_examples/ops_6/expected new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/test/conf_examples/ops_6/expected diff --git a/src/test/conf_examples/ops_6/torrc b/src/test/conf_examples/ops_6/torrc new file mode 100644 index 0000000000..4d51caaff7 --- /dev/null +++ b/src/test/conf_examples/ops_6/torrc @@ -0,0 +1,3 @@ +# This value has a slash, and so clears the one from defaults.torrc. + +/ORPort diff --git a/src/test/conf_examples/ops_6/torrc.defaults b/src/test/conf_examples/ops_6/torrc.defaults new file mode 100644 index 0000000000..04cd0393c6 --- /dev/null +++ b/src/test/conf_examples/ops_6/torrc.defaults @@ -0,0 +1 @@ +ORPort 9000 diff --git a/src/test/conf_examples/relpath_rad/error b/src/test/conf_examples/relpath_rad/error new file mode 100644 index 0000000000..e131744475 --- /dev/null +++ b/src/test/conf_examples/relpath_rad/error @@ -0,0 +1 @@ +RunAsDaemon is not compatible with relative paths. diff --git a/src/test/conf_examples/relpath_rad/torrc b/src/test/conf_examples/relpath_rad/torrc new file mode 100644 index 0000000000..fe02441c3f --- /dev/null +++ b/src/test/conf_examples/relpath_rad/torrc @@ -0,0 +1,4 @@ + +# Relative-path data directories are incompatible with RunAsDaemon +DataDirectory ./datadir +RunAsDaemon 1 diff --git a/src/test/fuzz/fuzz_hsdescv3.c b/src/test/fuzz/fuzz_hsdescv3.c index 2cbd655898..9d4a6dbb55 100644 --- a/src/test/fuzz/fuzz_hsdescv3.c +++ b/src/test/fuzz/fuzz_hsdescv3.c @@ -35,16 +35,21 @@ mock_rsa_ed25519_crosscert_check(const uint8_t *crosscert, static size_t mock_decrypt_desc_layer(const hs_descriptor_t *desc, - const uint8_t *encrypted_blob, - size_t encrypted_blob_size, const uint8_t *descriptor_cookie, - int is_superencrypted_layer, + bool is_superencrypted_layer, char **decrypted_out) { (void)is_superencrypted_layer; (void)desc; (void)descriptor_cookie; const size_t overhead = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN; + const uint8_t *encrypted_blob = (is_superencrypted_layer) + ? desc->plaintext_data.superencrypted_blob + : desc->superencrypted_data.encrypted_blob; + size_t encrypted_blob_size = (is_superencrypted_layer) + ? desc->plaintext_data.superencrypted_blob_size + : desc->superencrypted_data.encrypted_blob_size; + if (encrypted_blob_size < overhead) return 0; *decrypted_out = tor_memdup_nulterm( diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c index 6d0f9d7d60..604aba7a7f 100644 --- a/src/test/fuzz/fuzzing_common.c +++ b/src/test/fuzz/fuzzing_common.c @@ -1,6 +1,7 @@ /* Copyright (c) 2016-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define CRYPTO_ED25519_PRIVATE +#define CONFIG_PRIVATE #include "orconfig.h" #include "core/or/or.h" #include "app/main/subsysmgr.h" @@ -111,7 +112,7 @@ global_init(void) } /* set up the options. */ - mock_options = tor_malloc_zero(sizeof(or_options_t)); + mock_options = options_new(); MOCK(get_options, mock_get_options); /* Make BUG() and nonfatal asserts crash */ @@ -137,7 +138,7 @@ LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) return fuzz_main(Data, Size); } -#else /* !(defined(LLVM_FUZZ)) */ +#else /* !defined(LLVM_FUZZ) */ int main(int argc, char **argv) @@ -166,7 +167,7 @@ main(int argc, char **argv) memset(&s, 0, sizeof(s)); set_log_severity_config(loglevel, LOG_ERR, &s); /* ALWAYS log bug warnings. */ - s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; + s.masks[SEVERITY_MASK_IDX(LOG_WARN)] |= LD_BUG; add_stream_log(&s, "", fileno(stdout)); } @@ -189,7 +190,7 @@ main(int argc, char **argv) if (fuzz_cleanup() < 0) abort(); - tor_free(mock_options); + or_options_free(mock_options); UNMOCK(get_options); return 0; } diff --git a/src/test/include.am b/src/test/include.am index 85f9c9f880..d8e25dea9f 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -23,7 +23,9 @@ TESTSCRIPTS = \ src/test/test_workqueue_pipe.sh \ src/test/test_workqueue_pipe2.sh \ src/test/test_workqueue_socketpair.sh \ - src/test/test_switch_id.sh + src/test/test_switch_id.sh \ + src/test/test_cmdline.sh \ + src/test/test_parseconf.sh if USE_RUST TESTSCRIPTS += \ @@ -31,7 +33,11 @@ TESTSCRIPTS += \ endif if USEPYTHON -TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh +TESTSCRIPTS += \ + src/test/test_ntor.sh \ + src/test/test_hs_ntor.sh \ + src/test/test_bt.sh \ + scripts/maint/practracker/test_practracker.sh if COVERAGE_ENABLED # ... @@ -96,6 +102,7 @@ src_test_test_SOURCES += \ src/test/log_test_helpers.c \ src/test/hs_test_helpers.c \ src/test/rend_test_helpers.c \ + src/test/resolve_test_helpers.c \ src/test/rng_test_helpers.c \ src/test/test.c \ src/test/test_accounting.c \ @@ -120,6 +127,8 @@ src_test_test_SOURCES += \ src/test/test_circuitstats.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ + src/test/test_confmgr.c \ + src/test/test_confparse.c \ src/test/test_connection.c \ src/test/test_conscache.c \ src/test/test_consdiff.c \ @@ -153,6 +162,7 @@ src_test_test_SOURCES += \ src/test/test_handles.c \ src/test/test_hs_cache.c \ src/test/test_hs_descriptor.c \ + src/test/test_hs_dos.c \ src/test/test_introduce.c \ src/test/test_keypin.c \ src/test/test_link_handshake.c \ @@ -169,6 +179,7 @@ src_test_test_SOURCES += \ src/test/test_periodic_event.c \ src/test/test_policy.c \ src/test/test_process.c \ + src/test/test_process_descs.c \ src/test/test_prob_distr.c \ src/test/test_procmon.c \ src/test/test_proto_http.c \ @@ -193,6 +204,7 @@ src_test_test_SOURCES += \ src/test/test_status.c \ src/test/test_storagedir.c \ src/test/test_threads.c \ + src/test/test_token_bucket.c \ src/test/test_tortls.c \ src/test/test_util.c \ src/test/test_util_format.c \ @@ -330,6 +342,7 @@ noinst_HEADERS+= \ src/test/hs_test_helpers.h \ src/test/log_test_helpers.h \ src/test/rend_test_helpers.h \ + src/test/resolve_test_helpers.h \ src/test/rng_test_helpers.h \ src/test/test.h \ src/test/ptr_helpers.h \ @@ -404,7 +417,9 @@ EXTRA_DIST += \ src/test/test_workqueue_efd2.sh \ src/test/test_workqueue_pipe.sh \ src/test/test_workqueue_pipe2.sh \ - src/test/test_workqueue_socketpair.sh + src/test/test_workqueue_socketpair.sh \ + src/test/test_cmdline.sh \ + src/test/test_parseconf.sh test-rust: $(TESTS_ENVIRONMENT) "$(abs_top_srcdir)/src/test/test_rust.sh" diff --git a/src/test/resolve_test_helpers.c b/src/test/resolve_test_helpers.c new file mode 100644 index 0000000000..73ea730149 --- /dev/null +++ b/src/test/resolve_test_helpers.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file resolve_test_helpers.c + * @brief Helper functions for mocking libc's blocking hostname lookup + * facilities. + **/ + +#define RESOLVE_PRIVATE +#include "orconfig.h" +#include "test/resolve_test_helpers.h" +#include "lib/net/address.h" +#include "lib/net/resolve.h" +#include "test/test.h" + +#include <stdio.h> +#include <string.h> + +/** + * Mock replacement for our getaddrinfo/gethostbyname wrapper. + **/ +static int +replacement_host_lookup(const char *name, uint16_t family, tor_addr_t *addr) +{ + static const struct lookup_table_ent { + const char *name; + const char *ipv4; + const char *ipv6; + } entries[] = { + { "localhost", "127.0.0.1", "::1" }, + { "torproject.org", "198.51.100.6", "2001:DB8::700" }, + { NULL, NULL, NULL }, + }; + + int r = -1; + + for (unsigned i = 0; entries[i].name != NULL; ++i) { + if (!strcasecmp(name, entries[i].name)) { + if (family == AF_INET6) { + int s = tor_addr_parse(addr, entries[i].ipv6); + tt_int_op(s, OP_EQ, AF_INET6); + } else { + int s = tor_addr_parse(addr, entries[i].ipv4); + tt_int_op(s, OP_EQ, AF_INET); + } + r = 0; + break; + } + } + + log_debug(LD_GENERAL, "resolve(%s,%d) => %s", + name, family, r == 0 ? fmt_addr(addr) : "-1"); + + return r; + done: + return -1; +} + +/** + * Set up a mock replacement for our wrapper on libc's resolver code. + * + * According to our replacement, only "localhost" and "torproject.org" + * are real addresses; everything else doesn't exist. + * + * Use this function to avoid using the DNS resolver during unit tests; + * call unmock_hostname_resolver() when you're done. + **/ +void +mock_hostname_resolver(void) +{ + MOCK(tor_addr_lookup_host_impl, replacement_host_lookup); +} + +/** + * Unmock our wrappers for libc's blocking hostname resolver code. + **/ +void +unmock_hostname_resolver(void) +{ + UNMOCK(tor_addr_lookup_host_impl); +} diff --git a/src/test/resolve_test_helpers.h b/src/test/resolve_test_helpers.h new file mode 100644 index 0000000000..e7d2e29373 --- /dev/null +++ b/src/test/resolve_test_helpers.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file resolve_test_helpers.h + * @brief Header for test/resolve_test_helpers.c + **/ + +#ifndef TOR_TEST_RESOLVE_TEST_HELPERS_H +#define TOR_TEST_RESOLVE_TEST_HELPERS_H + +void mock_hostname_resolver(void); +void unmock_hostname_resolver(void); + +#endif /* !defined(TOR_TEST_RESOLVE_TEST_HELPERS_H) */ diff --git a/src/test/test.c b/src/test/test.c index cac98dd839..6dbec26fa8 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -840,6 +840,8 @@ struct testgroup_t testgroups[] = { { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, + { "config/mgr/", confmgr_tests }, + { "config/parse/", confparse_tests }, { "connection/", connection_tests }, { "conscache/", conscache_tests }, { "consdiff/", consdiff_tests }, @@ -857,6 +859,7 @@ struct testgroup_t testgroups[] = { { "crypto/pem/", pem_tests }, { "crypto/rng/", crypto_rng_tests }, { "dir/", dir_tests }, + { "dir/auth/process_descs/", process_descs_tests }, { "dir/md/", microdesc_tests }, { "dir/voting/flags/", voting_flags_tests }, { "dir/voting/schedule/", voting_schedule_tests }, @@ -876,6 +879,7 @@ struct testgroup_t testgroups[] = { { "hs_config/", hs_config_tests }, { "hs_control/", hs_control_tests }, { "hs_descriptor/", hs_descriptor }, + { "hs_dos/", hs_dos_tests }, { "hs_intropoint/", hs_intropoint_tests }, { "hs_ntor/", hs_ntor_tests }, { "hs_service/", hs_service_tests }, @@ -916,6 +920,7 @@ struct testgroup_t testgroups[] = { { "socks/", socks_tests }, { "status/" , status_tests }, { "storagedir/", storagedir_tests }, + { "token_bucket/", token_bucket_tests }, { "tortls/", tortls_tests }, #ifndef ENABLE_NSS { "tortls/openssl/", tortls_openssl_tests }, diff --git a/src/test/test.h b/src/test/test.h index 167fd090ac..76c4c0ec75 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -197,6 +197,8 @@ extern struct testcase_t circuitstats_tests[]; extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; +extern struct testcase_t confmgr_tests[]; +extern struct testcase_t confparse_tests[]; extern struct testcase_t connection_tests[]; extern struct testcase_t conscache_tests[]; extern struct testcase_t consdiff_tests[]; @@ -226,6 +228,7 @@ extern struct testcase_t hs_common_tests[]; extern struct testcase_t hs_config_tests[]; extern struct testcase_t hs_control_tests[]; extern struct testcase_t hs_descriptor[]; +extern struct testcase_t hs_dos_tests[]; extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t hs_ntor_tests[]; extern struct testcase_t hs_service_tests[]; @@ -250,6 +253,7 @@ extern struct testcase_t prob_distr_tests[]; extern struct testcase_t slow_stochastic_prob_distr_tests[]; extern struct testcase_t procmon_tests[]; extern struct testcase_t process_tests[]; +extern struct testcase_t process_descs_tests[]; extern struct testcase_t proto_http_tests[]; extern struct testcase_t proto_misc_tests[]; extern struct testcase_t protover_tests[]; @@ -272,6 +276,7 @@ extern struct testcase_t sr_tests[]; extern struct testcase_t status_tests[]; extern struct testcase_t storagedir_tests[]; extern struct testcase_t thread_tests[]; +extern struct testcase_t token_bucket_tests[]; extern struct testcase_t tortls_openssl_tests[]; extern struct testcase_t tortls_tests[]; extern struct testcase_t util_format_tests[]; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 05d8bf6c7b..c89c6e78d4 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -12,6 +12,7 @@ #include "test/log_test_helpers.h" #include "lib/net/resolve.h" #include "test/rng_test_helpers.h" +#include "test/resolve_test_helpers.h" #ifdef HAVE_SYS_UN_H #include <sys/un.h> @@ -724,158 +725,559 @@ test_addr_ip6_helpers(void *arg) ; } +/* Test that addr_str successfully parses, and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str. + */ +#define TEST_ADDR_PARSE_FMT(addr_str, expect_family, fmt_decorated, \ + expect_str) \ + STMT_BEGIN \ + r = tor_addr_parse(&addr, addr_str); \ + tt_int_op(r, OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + STMT_END + +/* Test that addr_str fails to parse, and: + * - the returned address is null. + */ +#define TEST_ADDR_PARSE_XFAIL(addr_str) \ + STMT_BEGIN \ + r = tor_addr_parse(&addr, addr_str); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that addr_port_str and default_port successfully parse, and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str, + * - the port is expect_port. + */ +#define TEST_ADDR_PORT_PARSE_FMT(addr_port_str, default_port, expect_family, \ + fmt_decorated, expect_str, expect_port) \ + STMT_BEGIN \ + r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \ + default_port); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + tt_int_op(port, OP_EQ, expect_port); \ + STMT_END + +/* Test that addr_port_str and default_port fail to parse, and: + * - the returned address is null, + * - the returned port is 0. + */ +#define TEST_ADDR_PORT_PARSE_XFAIL(addr_port_str, default_port) \ + STMT_BEGIN \ + r = tor_addr_port_parse(LOG_DEBUG, addr_port_str, &addr, &port, \ + default_port); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + STMT_END + +/* Test that addr_str successfully parses as an IPv4 address using + * tor_lookup_hostname(), and: + * - the fmt_addr32() of the result is expect_str. + */ +#define TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, expect_str) \ + STMT_BEGIN \ + r = tor_lookup_hostname(addr_str, &addr32h); \ + tt_int_op(r, OP_EQ, 0); \ + tt_str_op(fmt_addr32(addr32h), OP_EQ, expect_str); \ + STMT_END + +/* Test that bad_str fails to parse using tor_lookup_hostname(), with a + * permanent failure, and: + * - the returned address is 0. + */ +#define TEST_ADDR_V4_LOOKUP_XFAIL(bad_str) \ + STMT_BEGIN \ + r = tor_lookup_hostname(bad_str, &addr32h); \ + tt_int_op(r, OP_EQ, -1); \ + tt_int_op(addr32h, OP_EQ, 0); \ + STMT_END + +/* Test that looking up host_str as an IPv4 address using tor_lookup_hostname() + * does something sensible: + * - the result is -1, 0, or 1. + * - if the result is a failure, the returned address is 0. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_V4_LOOKUP(host_str) \ + STMT_BEGIN \ + r = tor_lookup_hostname(host_str, &addr32h); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 1); \ + if (r != 0) \ + tt_int_op(addr32h, OP_EQ, 0); \ + STMT_END + +/* Test that addr_str successfully parses as a require_family IP address using + * tor_addr_lookup(), and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str. + */ +#define TEST_ADDR_LOOKUP_FMT(addr_str, require_family, expect_family, \ + fmt_decorated, expect_str) \ + STMT_BEGIN \ + r = tor_addr_lookup(addr_str, require_family, &addr); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + STMT_END + +/* Test that bad_str fails to parse as a require_family IP address using + * tor_addr_lookup(), with a permanent failure, and: + * - the returned address is null. + */ +#define TEST_ADDR_LOOKUP_XFAIL(bad_str, require_family) \ + STMT_BEGIN \ + r = tor_addr_lookup(bad_str, require_family, &addr); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that looking up host_string as a require_family IP address using + * tor_addr_lookup(), does something sensible: + * - the result is -1, 0, or 1. + * - if the result is a failure, the returned address is null. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_LOOKUP(host_str, require_family) \ + STMT_BEGIN \ + r = tor_addr_lookup(host_str, require_family, &addr); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 1); \ + if (r != 0) \ + tt_assert(tor_addr_is_null(&addr)); \ + STMT_END + +/* Test that addr_port_str successfully parses as an IP address and port + * using tor_addr_port_lookup(), and: + * - the address has family expect_family, + * - the fmt_decorated result of tor_addr_to_str() is expect_str, + * - the port is expect_port. + */ +#define TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, expect_family, \ + fmt_decorated, expect_str, expect_port) \ + STMT_BEGIN \ + r = tor_addr_port_lookup(addr_port_str, &addr, &port); \ + tt_int_op(r, OP_EQ, 0); \ + tt_int_op(tor_addr_family(&addr), OP_EQ, expect_family); \ + sv = tor_addr_to_str(buf, &addr, sizeof(buf), fmt_decorated); \ + tt_str_op(sv, OP_EQ, buf); \ + tt_str_op(buf, OP_EQ, expect_str); \ + tt_int_op(port, OP_EQ, expect_port); \ + STMT_END + +/* Test that bad_str fails to parse as an IP address and port + * using tor_addr_port_lookup(), and: + * - the returned address is null, + * - the returned port is 0. + */ +#define TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str) \ + STMT_BEGIN \ + r = tor_addr_port_lookup(bad_str, &addr, &port); \ + tt_int_op(r, OP_EQ, -1); \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + STMT_END + +/* Test that looking up host_port_str as an IP address using + * tor_addr_port_lookup(), does something sensible: + * - the result is -1 or 0. + * - if the result is a failure, the returned address is null, and the + * returned port is zero, + * - if the result is a success, the returned port is expect_success_port, + * and the returned family is AF_INET or AF_INET6. + * We can't rely on the result of this function, because it depends on the + * network. + */ +#define TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port) \ + STMT_BEGIN \ + r = tor_addr_port_lookup(host_port_str, &addr, &port); \ + tt_int_op(r, OP_GE, -1); \ + tt_int_op(r, OP_LE, 0); \ + if (r == -1) { \ + tt_assert(tor_addr_is_null(&addr)); \ + tt_int_op(port, OP_EQ, 0); \ + } else { \ + tt_assert(tor_addr_family(&addr) == AF_INET || \ + tor_addr_family(&addr) == AF_INET6); \ + tt_int_op(port, OP_EQ, expect_success_port); \ + } \ + STMT_END + +/* Test that addr_str successfully parses as a canonical IPv4 address. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), + * - tor_addr_lookup() with AF_INET, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_port_lookup(), because there is no port. + */ +#define TEST_ADDR_V4_PARSE_CANONICAL(addr_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET, 0, addr_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 111, AF_INET, 0, \ + addr_str, 111); \ + TEST_ADDR_V4_LOOKUP_HOSTNAME(addr_str, addr_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET, 0, addr_str, 0); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET, AF_INET, 0, addr_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET, 0, addr_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET6); \ + STMT_END + +/* Test that addr_str successfully parses as a canonical fmt_decorated + * IPv6 address. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port, + * - tor_lookup_hostname(), because it only supports IPv4, + * - tor_addr_lookup() with AF_INET. + */ +#define TEST_ADDR_V6_PARSE_CANONICAL(addr_str, fmt_decorated) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, addr_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 222, AF_INET6, fmt_decorated, \ + addr_str, 222); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \ + addr_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \ + addr_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, addr_str, \ + 0); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \ + STMT_END + +/* Test that addr_str successfully parses, and the fmt_decorated canonical + * IPv6 string is expect_str. + * Check for successful parsing using: + * - tor_addr_parse(), + * - tor_addr_port_parse() with a default port, + * - tor_addr_lookup() with AF_INET6, + * - tor_addr_lookup() with AF_UNSPEC, + * - tor_addr_port_lookup(), with a zero port. + * Check for failures using: + * - tor_addr_port_parse() without a default port, because there is no port. + * - tor_lookup_hostname(), because it only supports IPv4, + * - tor_addr_lookup() with AF_INET. + */ +#define TEST_ADDR_V6_PARSE(addr_str, fmt_decorated, expect_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_FMT(addr_str, AF_INET6, fmt_decorated, expect_str); \ + TEST_ADDR_PORT_PARSE_FMT(addr_str, 333, AF_INET6, fmt_decorated, \ + expect_str, 333); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_INET6, AF_INET6, fmt_decorated, \ + expect_str); \ + TEST_ADDR_LOOKUP_FMT(addr_str, AF_UNSPEC, AF_INET6, fmt_decorated, \ + expect_str); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_str, AF_INET6, fmt_decorated, expect_str, \ + 0); \ + TEST_ADDR_PORT_PARSE_XFAIL(addr_str, -1); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_str, AF_INET); \ + STMT_END + +/* Test that addr_port_str successfully parses to the canonical IPv4 address + * string expect_str, and port expect_port. + * Check for successful parsing using: + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_addr_port_lookup(). + * Check for failures using: + * - tor_addr_parse(), because there is a port, + * - tor_lookup_hostname(), because there is a port. + * - tor_addr_lookup(), regardless of the address family, because there is a + * port. + */ +#define TEST_ADDR_V4_PORT_PARSE(addr_port_str, expect_str, expect_port) \ + STMT_BEGIN \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 444, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PARSE_XFAIL(addr_port_str); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \ + STMT_END + +/* Test that addr_port_str successfully parses to the canonical undecorated + * IPv6 address string expect_str, and port expect_port. + * Check for successful parsing using: + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_addr_port_lookup(). + * Check for failures using: + * - tor_addr_parse(), because there is a port, + * - tor_lookup_hostname(), because there is a port, and because it only + * supports IPv4, + * - tor_addr_lookup(), regardless of the address family, because there is a + * port. + */ +#define TEST_ADDR_V6_PORT_PARSE(addr_port_str, expect_str, expect_port) \ + STMT_BEGIN \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, -1, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_PARSE_FMT(addr_port_str, 555, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PORT_LOOKUP_FMT(addr_port_str, AF_INET6, 0, expect_str, \ + expect_port); \ + TEST_ADDR_PARSE_XFAIL(addr_port_str); \ + TEST_ADDR_V4_LOOKUP_XFAIL(addr_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET6); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(addr_port_str, AF_INET); \ + STMT_END + +/* Test that bad_str fails to parse due to a bad address or port. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), + * - tor_addr_lookup(), regardless of the address family, + * - tor_addr_port_lookup(). + */ +#define TEST_ADDR_PARSE_XFAIL_MALFORMED(bad_str) \ + STMT_BEGIN \ + TEST_ADDR_PARSE_XFAIL(bad_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(bad_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(bad_str, 666); \ + TEST_ADDR_V4_LOOKUP_XFAIL(bad_str); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(bad_str, AF_INET6); \ + TEST_ADDR_PORT_LOOKUP_XFAIL(bad_str); \ + STMT_END + +/* Test that host_str is treated as a hostname, and not an address. + * Check for success or failure using the network-dependent functions: + * - tor_lookup_hostname(), + * - tor_addr_lookup(), regardless of the address family, + * - tor_addr_port_lookup(), expecting a zero port. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port. + */ +#define TEST_HOSTNAME(host_str) \ + STMT_BEGIN \ + TEST_HOST_V4_LOOKUP(host_str); \ + TEST_HOST_LOOKUP(host_str, AF_UNSPEC); \ + TEST_HOST_LOOKUP(host_str, AF_INET); \ + TEST_HOST_LOOKUP(host_str, AF_INET6); \ + TEST_HOST_PORT_LOOKUP(host_str, 0); \ + TEST_ADDR_PARSE_XFAIL(host_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_str, 777); \ + STMT_END + +/* Test that host_port_str is treated as a hostname and port, and not a + * hostname or an address. + * Check for success or failure using the network-dependent function: + * - tor_addr_port_lookup(), expecting expect_success_port if the lookup is + * successful. + * Check for failures using: + * - tor_addr_parse(), + * - tor_addr_port_parse() without a default port, + * - tor_addr_port_parse() with a default port, + * - tor_lookup_hostname(), because it doesn't support ports, + * - tor_addr_lookup(), regardless of the address family, because it doesn't + * support ports. + */ +#define TEST_HOSTNAME_PORT(host_port_str, expect_success_port) \ + STMT_BEGIN \ + TEST_HOST_PORT_LOOKUP(host_port_str, expect_success_port); \ + TEST_ADDR_PARSE_XFAIL(host_port_str); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, -1); \ + TEST_ADDR_PORT_PARSE_XFAIL(host_port_str, 888); \ + TEST_ADDR_V4_LOOKUP_XFAIL(host_port_str); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_UNSPEC); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET); \ + TEST_ADDR_LOOKUP_XFAIL(host_port_str, AF_INET6); \ + STMT_END + +static void +test_addr_parse_canonical(void *arg) +{ + int r; + tor_addr_t addr; + uint16_t port; + const char *sv; + uint32_t addr32h; + char buf[TOR_ADDR_BUF_LEN]; + + (void)arg; + + /* Correct calls. */ + TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.1"); + TEST_ADDR_V4_PARSE_CANONICAL("192.0.2.2"); + + TEST_ADDR_V6_PARSE_CANONICAL("[11:22::33:44]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[::1]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[::]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[2::]", 1); + TEST_ADDR_V6_PARSE_CANONICAL("[11:22:33:44:55:66:77:88]", 1); + + /* Allow IPv6 without square brackets, when there is no port, but only if + * there is a default port */ + TEST_ADDR_V6_PARSE_CANONICAL("11:22::33:44", 0); + TEST_ADDR_V6_PARSE_CANONICAL("::1", 0); + TEST_ADDR_V6_PARSE_CANONICAL("::", 0); + TEST_ADDR_V6_PARSE_CANONICAL("2::", 0); + TEST_ADDR_V6_PARSE_CANONICAL("11:22:33:44:55:66:77:88", 0); + done: + ; +} + /** Test tor_addr_parse() and tor_addr_port_parse(). */ static void test_addr_parse(void *arg) { + int r; tor_addr_t addr; + uint16_t port; + const char *sv; + uint32_t addr32h; char buf[TOR_ADDR_BUF_LEN]; - uint16_t port = 0; - /* Correct call. */ (void)arg; - r= tor_addr_parse(&addr, "192.0.2.1"); - tt_int_op(r,OP_EQ, AF_INET); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "192.0.2.1"); - - r= tor_addr_parse(&addr, "11:22::33:44"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44"); - - r= tor_addr_parse(&addr, "[11:22::33:44]"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44"); - - r= tor_addr_parse(&addr, "11:22:33:44:55:66:1.2.3.4"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22:33:44:55:66:102:304"); - - r= tor_addr_parse(&addr, "11:22::33:44:1.2.3.4"); - tt_int_op(r,OP_EQ, AF_INET6); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "11:22::33:44:102:304"); - /* Empty string. */ - r= tor_addr_parse(&addr, ""); - tt_int_op(r,OP_EQ, -1); + mock_hostname_resolver(); - /* Square brackets around IPv4 address. */ - r= tor_addr_parse(&addr, "[192.0.2.1]"); - tt_int_op(r,OP_EQ, -1); + /* IPv6-mapped IPv4 addresses. Tor doesn't really use these. */ + TEST_ADDR_V6_PARSE("11:22:33:44:55:66:1.2.3.4", 0, + "11:22:33:44:55:66:102:304"); - /* Only left square bracket. */ - r= tor_addr_parse(&addr, "[11:22::33:44"); - tt_int_op(r,OP_EQ, -1); + TEST_ADDR_V6_PARSE("11:22::33:44:1.2.3.4", 0, + "11:22::33:44:102:304"); - /* Only right square bracket. */ - r= tor_addr_parse(&addr, "11:22::33:44]"); - tt_int_op(r,OP_EQ, -1); + /* Ports. */ + TEST_ADDR_V4_PORT_PARSE("192.0.2.1:1234", "192.0.2.1", 1234); + TEST_ADDR_V6_PORT_PARSE("[::1]:1234", "::1", 1234); - /* Leading colon. */ - r= tor_addr_parse(&addr, ":11:22::33:44"); - tt_int_op(r,OP_EQ, -1); + /* Host names. */ + TEST_HOSTNAME("localhost"); + TEST_HOSTNAME_PORT("localhost:1234", 1234); + TEST_HOSTNAME_PORT("localhost:0", 0); - /* Trailing colon. */ - r= tor_addr_parse(&addr, "11:22::33:44:"); - tt_int_op(r,OP_EQ, -1); - - /* Too many hex words in IPv4-mapped IPv6 address. */ - r= tor_addr_parse(&addr, "11:22:33:44:55:66:77:88:1.2.3.4"); - tt_int_op(r,OP_EQ, -1); - - /* Correct call. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.1:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, 0); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "192.0.2.1"); - tt_int_op(port,OP_EQ, 1234); - - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, 0); - tor_addr_to_str(buf, &addr, sizeof(buf), 0); - tt_str_op(buf,OP_EQ, "::1"); - tt_int_op(port,OP_EQ, 1234); - - /* Domain name. */ - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + TEST_HOSTNAME("torproject.org"); + TEST_HOSTNAME_PORT("torproject.org:56", 56); - /* Only IP. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + TEST_HOSTNAME("probably-not-a-valid-dns.name-tld"); + TEST_HOSTNAME_PORT("probably-not-a-valid-dns.name-tld:789", 789); - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,200); - - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Malformed addresses. */ + /* Empty string. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED(""); - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]", - &addr, &port, 400); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,400); + /* Square brackets around IPv4 address. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.1]"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[192.0.2.3]:12345"); - /* Bad port. */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:66666", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:66666", - &addr, &port, 200); - tt_int_op(r, OP_EQ, -1); + /* Only left square bracket. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("[11:22::33:44"); - /* Only domain name */ - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); - r= tor_addr_port_parse(LOG_DEBUG, - "torproject.org", - &addr, &port, 200); - tt_int_op(r, OP_EQ, -1); + /* Only right square bracket. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44]"); - /* Bad IP address */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2:1234", - &addr, &port, -1); - tt_int_op(r, OP_EQ, -1); + /* Leading colon. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED(":11:22::33:44"); - /* Make sure that the default port has lower priority than the real - one */ - r= tor_addr_port_parse(LOG_DEBUG, - "192.0.2.2:1337", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,1337); + /* Trailing colon. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("11:22::33:44:"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:"); - r= tor_addr_port_parse(LOG_DEBUG, - "[::1]:1369", - &addr, &port, 200); - tt_int_op(r, OP_EQ, 0); - tt_int_op(port,OP_EQ,1369); + /* Bad port. */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:66666"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:77777"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:88888"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:99999"); + + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:-1"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:-2"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:-3"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:-4"); + + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:1 bad"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("192.0.2.2:bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("[::1]:bad-port-1"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("::1:1-bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("localhost:1-bad-port-1"); + + /* Bad hostname */ + TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid"); + TEST_ADDR_PARSE_XFAIL_MALFORMED("definitely invalid:22222"); + + /* Ambiguous cases */ + /* Too many hex words in IPv4-mapped IPv6 address. + * But some OS host lookup routines accept it as a hostname, or + * as an IP address?? (I assume they discard unused characters). */ + TEST_HOSTNAME("11:22:33:44:55:66:77:88:1.2.3.4"); + + /* IPv6 address with port and no brackets + * We reject it, but some OS host lookup routines accept it as an + * IPv6 address:port ? */ + TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345); + /* Is it a port, or are there too many hex words? + * We reject it either way, but some OS host lookup routines accept it as an + * IPv6 address:port */ + TEST_HOSTNAME_PORT("11:22:33:44:55:66:77:88:99", 99); + /* But we accept it if it has square brackets. */ + TEST_ADDR_V6_PORT_PARSE("[11:22:33:44:55:66:77:88]:99", + "11:22:33:44:55:66:77:88",99); + + /* Bad IPv4 address + * We reject it, but some OS host lookup routines accept it as an + * IPv4 address[:port], with a zero last octet */ + TEST_HOSTNAME("192.0.1"); + TEST_HOSTNAME_PORT("192.0.2:1234", 1234); + + /* More bad IPv6 addresses and ports: no brackets + * We reject it, but some OS host lookup routines accept it as an + * IPv6 address[:port] */ + TEST_HOSTNAME_PORT("::1:12345", 12345); + TEST_HOSTNAME_PORT("11:22::33:44:12345", 12345); + + /* And this is an ambiguous case, which is interpreted as an IPv6 address. */ + TEST_ADDR_V6_PARSE_CANONICAL("11:22::88:99", 0); + /* Use square brackets to resolve the ambiguity */ + TEST_ADDR_V6_PARSE_CANONICAL("[11:22::88:99]", 1); + TEST_ADDR_V6_PORT_PARSE("[11:22::88]:99", + "11:22::88",99); done: - ; + unmock_hostname_resolver(); } static void @@ -1254,6 +1656,7 @@ struct testcase_t addr_tests[] = { ADDR_LEGACY(basic), ADDR_LEGACY(ip6_helpers), ADDR_LEGACY(parse), + ADDR_LEGACY(parse_canonical), { "virtaddr", test_virtaddrmap, 0, NULL, NULL }, { "virtaddr_persist", test_virtaddrmap_persist, TT_FORK, NULL, NULL }, { "localname", test_addr_localname, 0, NULL, NULL }, diff --git a/src/test/test_address.c b/src/test/test_address.c index bf9ca047dc..32fb4aa232 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -24,6 +24,8 @@ #endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */ #include "core/or/or.h" +#include "app/config/config.h" +#include "feature/dirauth/process_descs.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/node_st.h" #include "feature/nodelist/nodelist.h" @@ -1244,8 +1246,95 @@ test_address_tor_node_in_same_network_family(void *ignored) helper_free_mock_node(node_b); } +static or_options_t mock_options; + +static const or_options_t * +mock_get_options(void) +{ + return &mock_options; +} + +/* Test dirserv_router_has_valid_address() on a stub routerinfo, with only its + * address fields set. Use IPv4 ipv4_addr_str and IPv6 ipv6_addr_str. + * Fail if it does not return rv. */ +#define TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, ipv6_addr_str, rv) \ + STMT_BEGIN \ + ri = tor_malloc_zero(sizeof(routerinfo_t)); \ + tor_addr_t addr; \ + tor_addr_parse(&addr, (ipv4_addr_str)); \ + ri->addr = tor_addr_to_ipv4h(&addr); \ + tor_addr_parse(&ri->ipv6_addr, (ipv6_addr_str)); \ + tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, (rv)); \ + tor_free(ri); \ + STMT_END + +/* Like TEST_ROUTER_VALID_ADDRESS_HELPER(), but always passes a null + * IPv6 address. */ +#define CHECK_RI_ADDR(ipv4_addr_str, rv) \ + TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, "::", rv) + +/* Like TEST_ROUTER_VALID_ADDRESS_HELPER(), but always passes a non-internal + * IPv4 address, so that the IPv6 check is reached. */ +#define CHECK_RI_ADDR6(ipv6_addr_str, rv) \ + TEST_ROUTER_VALID_ADDRESS_HELPER("1.0.0.1", ipv6_addr_str, rv) + +static void +test_address_dirserv_router_addr_private(void *opt_dir_allow_private) +{ + /* A stub routerinfo structure, with only its address fields set. */ + routerinfo_t *ri = NULL; + /* The expected return value for private addresses. + * Modified if DirAllowPrivateAddresses is 1. */ + int private_rv = -1; + + memset(&mock_options, 0, sizeof(or_options_t)); + MOCK(get_options, mock_get_options); + + if (opt_dir_allow_private) { + mock_options.DirAllowPrivateAddresses = 1; + private_rv = 0; + } + + CHECK_RI_ADDR("1.0.0.1", 0); + CHECK_RI_ADDR("10.0.0.1", private_rv); + + CHECK_RI_ADDR6("2600::1", 0); + CHECK_RI_ADDR6("fe80::1", private_rv); + + /* Null addresses */ + /* IPv4 null fails, regardless of IPv6 */ + CHECK_RI_ADDR("0.0.0.0", private_rv); + TEST_ROUTER_VALID_ADDRESS_HELPER("0.0.0.0", "::", private_rv); + + /* IPv6 null succeeds, because IPv4 is not null */ + CHECK_RI_ADDR6("::", 0); + + /* Byte-zeroed null addresses */ + /* IPv4 null fails, regardless of IPv6 */ + { + ri = tor_malloc_zero(sizeof(routerinfo_t)); + tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, private_rv); + tor_free(ri); + } + + /* IPv6 null succeeds, because IPv4 is not internal */ + { + ri = tor_malloc_zero(sizeof(routerinfo_t)); + ri->addr = 16777217; /* 1.0.0.1 */ + tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, 0); + tor_free(ri); + } + + done: + tor_free(ri); + UNMOCK(get_options); +} + #define ADDRESS_TEST(name, flags) \ { #name, test_address_ ## name, flags, NULL, NULL } +#define ADDRESS_TEST_STR_ARG(name, flags, str_arg) \ + { #name "/" str_arg, test_address_ ## name, flags, &passthrough_setup, \ + (void *)(str_arg) } struct testcase_t address_tests[] = { ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK), @@ -1277,5 +1366,7 @@ struct testcase_t address_tests[] = { ADDRESS_TEST(tor_addr_eq_ipv4h, 0), ADDRESS_TEST(tor_addr_in_same_network_family, 0), ADDRESS_TEST(tor_node_in_same_network_family, 0), + ADDRESS_TEST(dirserv_router_addr_private, 0), + ADDRESS_TEST_STR_ARG(dirserv_router_addr_private, 0, "allow_private"), END_OF_TESTCASES }; diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c index fb8408b3c3..6e299d779e 100644 --- a/src/test/test_address_set.c +++ b/src/test/test_address_set.c @@ -4,6 +4,7 @@ #include "core/or/or.h" #include "lib/crypt_ops/crypto_rand.h" #include "core/or/address_set.h" +#include "feature/nodelist/dirlist.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" @@ -31,6 +32,12 @@ mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) return dummy_ns; } +static void +mock_dirlist_add_trusted_dir_addresses(void) +{ + return; +} + /* Number of address a single node_t can have. Default to the production * value. This is to control the size of the bloom filter. */ static int addr_per_node = 2; @@ -98,6 +105,8 @@ test_nodelist(void *arg) mock_networkstatus_get_latest_consensus_by_flavor); MOCK(get_estimated_address_per_node, mock_get_estimated_address_per_node); + MOCK(dirlist_add_trusted_dir_addresses, + mock_dirlist_add_trusted_dir_addresses); dummy_ns = tor_malloc_zero(sizeof(*dummy_ns)); dummy_ns->flavor = FLAV_MICRODESC; @@ -113,7 +122,10 @@ test_nodelist(void *arg) * (the_nodelist->node_addrs) so we will fail the contain test rarely. */ addr_per_node = 1024; - /* No node no nothing. The lookups should be empty. */ + /* No node no nothing. The lookups should be empty. We've mocked the + * dirlist_add_trusted_dir_addresses in order for _no_ authorities to be + * added to the filter else it makes this test to trigger many false + * positive. */ nodelist_set_consensus(dummy_ns); /* The address set should be empty. */ @@ -167,6 +179,7 @@ test_nodelist(void *arg) UNMOCK(networkstatus_get_latest_consensus); UNMOCK(networkstatus_get_latest_consensus_by_flavor); UNMOCK(get_estimated_address_per_node); + UNMOCK(dirlist_add_trusted_dir_addresses); } struct testcase_t address_set_tests[] = { diff --git a/src/test/test_btrack.c b/src/test/test_btrack.c index 48486fb5a1..80da7829ae 100644 --- a/src/test/test_btrack.c +++ b/src/test/test_btrack.c @@ -4,6 +4,7 @@ #include "core/or/or.h" #include "test/test.h" +#include "test/test_helpers.h" #include "test/log_test_helpers.h" #define OCIRC_EVENT_PRIVATE @@ -12,48 +13,75 @@ #include "core/or/orconn_event.h" static void +send_state(const orconn_state_msg_t *msg_in) +{ + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + orconn_state_publish(msg); +} + +static void +send_status(const orconn_status_msg_t *msg_in) +{ + orconn_status_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + orconn_status_publish(msg); +} + +static void +send_chan(const ocirc_chan_msg_t *msg_in) +{ + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + ocirc_chan_publish(msg); +} + +static void test_btrack_launch(void *arg) { - orconn_event_msg_t conn; - ocirc_event_msg_t circ; + orconn_state_msg_t conn; + ocirc_chan_msg_t circ; + memset(&conn, 0, sizeof(conn)); + memset(&circ, 0, sizeof(circ)); (void)arg; - conn.type = ORCONN_MSGTYPE_STATE; - conn.u.state.gid = 1; - conn.u.state.chan = 1; - conn.u.state.proxy_type = PROXY_NONE; - conn.u.state.state = OR_CONN_STATE_CONNECTING; + conn.gid = 1; + conn.chan = 1; + conn.proxy_type = PROXY_NONE; + conn.state = OR_CONN_STATE_CONNECTING; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&conn); expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0 state=1"); expect_no_log_msg_containing("ORCONN BEST_"); teardown_capture_of_logs(); - circ.type = OCIRC_MSGTYPE_CHAN; - circ.u.chan.chan = 1; - circ.u.chan.onehop = true; + circ.chan = 1; + circ.onehop = true; setup_full_capture_of_logs(LOG_DEBUG); - ocirc_event_publish(&circ); + send_chan(&circ); expect_log_msg_containing("ORCONN LAUNCH chan=1 onehop=1"); expect_log_msg_containing("ORCONN BEST_ANY state -1->1 gid=1"); teardown_capture_of_logs(); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&conn); expect_log_msg_containing("ORCONN gid=2 chan=2 proxy_type=0 state=1"); expect_no_log_msg_containing("ORCONN BEST_"); teardown_capture_of_logs(); - circ.u.chan.chan = 2; - circ.u.chan.onehop = false; + circ.chan = 2; + circ.onehop = false; setup_full_capture_of_logs(LOG_DEBUG); - ocirc_event_publish(&circ); + send_chan(&circ); expect_log_msg_containing("ORCONN LAUNCH chan=2 onehop=0"); expect_log_msg_containing("ORCONN BEST_AP state -1->1 gid=2"); teardown_capture_of_logs(); @@ -65,27 +93,28 @@ test_btrack_launch(void *arg) static void test_btrack_delete(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t state; + orconn_status_msg_t status; + memset(&state, 0, sizeof(state)); + memset(&status, 0, sizeof(status)); (void)arg; - conn.type = ORCONN_MSGTYPE_STATE; - conn.u.state.gid = 1; - conn.u.state.chan = 1; - conn.u.state.proxy_type = PROXY_NONE; - conn.u.state.state = OR_CONN_STATE_CONNECTING; + state.gid = 1; + state.chan = 1; + state.proxy_type = PROXY_NONE; + state.state = OR_CONN_STATE_CONNECTING; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_state(&state); expect_log_msg_containing("ORCONN gid=1 chan=1 proxy_type=0"); teardown_capture_of_logs(); - conn.type = ORCONN_MSGTYPE_STATUS; - conn.u.status.gid = 1; - conn.u.status.status = OR_CONN_EVENT_CLOSED; - conn.u.status.reason = 0; + status.gid = 1; + status.status = OR_CONN_EVENT_CLOSED; + status.reason = 0; setup_full_capture_of_logs(LOG_DEBUG); - orconn_event_publish(&conn); + send_status(&status); expect_log_msg_containing("ORCONN DELETE gid=1 status=3 reason=0"); teardown_capture_of_logs(); @@ -94,7 +123,7 @@ test_btrack_delete(void *arg) } struct testcase_t btrack_tests[] = { - { "launch", test_btrack_launch, TT_FORK, 0, NULL }, - { "delete", test_btrack_delete, TT_FORK, 0, NULL }, + { "launch", test_btrack_launch, TT_FORK, &helper_pubsub_setup, NULL }, + { "delete", test_btrack_delete, TT_FORK, &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c index 5a013aa268..e6f028ed74 100644 --- a/src/test/test_bwmgt.c +++ b/src/test/test_bwmgt.c @@ -6,18 +6,67 @@ * \brief tests for bandwidth management / token bucket functions */ +#define CONFIG_PRIVATE +#define CONNECTION_PRIVATE #define TOKEN_BUCKET_PRIVATE #include "core/or/or.h" -#include "test/test.h" +#include "app/config/config.h" +#include "core/mainloop/connection.h" +#include "feature/dircommon/directory.h" +#include "feature/nodelist/microdesc.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerlist.h" +#include "lib/crypt_ops/crypto_rand.h" #include "lib/evloop/token_bucket.h" +#include "test/test.h" +#include "test/test_helpers.h" + +#include "app/config/or_options_st.h" +#include "core/or/connection_st.h" +#include "feature/nodelist/microdesc_st.h" +#include "feature/nodelist/networkstatus_st.h" +#include "feature/nodelist/routerinfo_st.h" +#include "feature/nodelist/routerstatus_st.h" // an imaginary time, in timestamp units. Chosen so it will roll over. static const uint32_t START_TS = UINT32_MAX-10; static const int32_t KB = 1024; static const uint32_t GB = (UINT64_C(1) << 30); +static or_options_t mock_options; + +static const or_options_t * +mock_get_options(void) +{ + return &mock_options; +} + +static networkstatus_t *dummy_ns = NULL; +static networkstatus_t * +mock_networkstatus_get_latest_consensus(void) +{ + return dummy_ns; +} + +static networkstatus_t * +mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) +{ + tor_assert(f == FLAV_MICRODESC); + return dummy_ns; +} + +/* Number of address a single node_t can have. Default to the production + * value. This is to control the size of the bloom filter. */ +static int addr_per_node = 2; +static int +mock_get_estimated_address_per_node(void) +{ + return addr_per_node; +} + static void test_bwmgt_token_buf_init(void *arg) { @@ -220,8 +269,162 @@ test_bwmgt_token_buf_helpers(void *arg) ; } +static void +test_bwmgt_dir_conn_global_write_low(void *arg) +{ + bool ret; + int addr_family; + connection_t *conn = NULL; + routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL; + tor_addr_t relay_addr; + + (void) arg; + + memset(&mock_options, 0, sizeof(or_options_t)); + MOCK(networkstatus_get_latest_consensus, + mock_networkstatus_get_latest_consensus); + MOCK(networkstatus_get_latest_consensus_by_flavor, + mock_networkstatus_get_latest_consensus_by_flavor); + MOCK(get_estimated_address_per_node, + mock_get_estimated_address_per_node); + + /* + * The following is rather complex but that is what it takes to add a dummy + * consensus with a valid routerlist which will populate our node address + * set that we need to lookup to test the known relay code path. + * + * We MUST do that before we MOCK(get_options) else it is another world of + * complexity. + */ + + /* This will be the address of our relay. */ + tor_addr_parse(&relay_addr, "1.2.3.4"); + + /* We'll now add a relay into our routerlist and see if we let it. */ + dummy_ns = tor_malloc_zero(sizeof(*dummy_ns)); + dummy_ns->flavor = FLAV_MICRODESC; + dummy_ns->routerstatus_list = smartlist_new(); + + md = tor_malloc_zero(sizeof(*md)); + ri = tor_malloc_zero(sizeof(*ri)); + rs = tor_malloc_zero(sizeof(*rs)); + crypto_rand(rs->identity_digest, sizeof(rs->identity_digest)); + crypto_rand(md->digest, sizeof(md->digest)); + memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN); + + /* Set IP address. */ + rs->addr = tor_addr_to_ipv4h(&relay_addr); + ri->addr = rs->addr; + /* Add the rs to the consensus becoming a node_t. */ + smartlist_add(dummy_ns->routerstatus_list, rs); + + /* Add all configured authorities (hardcoded) before we set the consensus so + * the address set exists. */ + ret = consider_adding_dir_servers(&mock_options, &mock_options); + tt_int_op(ret, OP_EQ, 0); + + /* This will make the nodelist bloom filter very large + * (the_nodelist->node_addrs) so we will fail the contain test rarely. */ + addr_per_node = 1024; + + nodelist_set_consensus(dummy_ns); + + /* Ok, now time to control which options we use. */ + MOCK(get_options, mock_get_options); + + /* Set ourselves as an authoritative dir. */ + mock_options.AuthoritativeDir = 1; + mock_options.V3AuthoritativeDir = 1; + mock_options.UseDefaultFallbackDirs = 0; + + /* This will set our global bucket to 1 byte and thus we will hit the + * banwdith limit in our test. */ + mock_options.BandwidthRate = 1; + mock_options.BandwidthBurst = 1; + + /* Else an IPv4 address screams. */ + mock_options.ClientUseIPv4 = 1; + mock_options.ClientUseIPv6 = 1; + + /* Initialize the global buckets. */ + connection_bucket_init(); + + /* The address "127.0.0.1" is set with this helper. */ + conn = test_conn_get_connection(DIR_CONN_STATE_MIN_, CONN_TYPE_DIR, + DIR_PURPOSE_MIN_); + tt_assert(conn); + + /* First try a non authority non relay IP thus a client but we are not + * configured to reject requests under load so we should get a false value + * that our limit is _not_ low. */ + addr_family = tor_addr_parse(&conn->addr, "1.1.1.1"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Now, we will reject requests under load so try again a non authority non + * relay IP thus a client. We should get a warning that our limit is too + * low. */ + mock_options.AuthDirRejectRequestsUnderLoad = 1; + + addr_family = tor_addr_parse(&conn->addr, "1.1.1.1"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 1); + + /* Now, lets try with a connection address from moria1. It should always + * pass even though our limit is too low. */ + addr_family = tor_addr_parse(&conn->addr, "128.31.0.39"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* IPv6 testing of gabelmoo. */ + addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]"); + tt_int_op(addr_family, OP_EQ, AF_INET6); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Lets retry with a known relay address. It should pass. Possible due to + * our consensus setting above. */ + memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t)); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Lets retry with a random IP that is not an authority nor a relay. */ + addr_family = tor_addr_parse(&conn->addr, "1.2.3.4"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 0); + + /* Finally, just make sure it still denies an IP if we are _not_ a v3 + * directory authority. */ + mock_options.V3AuthoritativeDir = 0; + addr_family = tor_addr_parse(&conn->addr, "1.2.3.4"); + tt_int_op(addr_family, OP_EQ, AF_INET); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 1); + + /* Random IPv6 should not be allowed. */ + addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]"); + tt_int_op(addr_family, OP_EQ, AF_INET6); + ret = connection_dir_is_global_write_low(conn, INT_MAX); + tt_int_op(ret, OP_EQ, 1); + + done: + connection_free_minimal(conn); + routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md); + smartlist_clear(dummy_ns->routerstatus_list); + networkstatus_vote_free(dummy_ns); + + UNMOCK(get_estimated_address_per_node); + UNMOCK(networkstatus_get_latest_consensus); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); + UNMOCK(get_options); +} + #define BWMGT(name) \ - { #name, test_bwmgt_ ## name , 0, NULL, NULL } + { #name, test_bwmgt_ ## name , TT_FORK, NULL, NULL } struct testcase_t bwmgt_tests[] = { BWMGT(token_buf_init), @@ -229,5 +432,7 @@ struct testcase_t bwmgt_tests[] = { BWMGT(token_buf_dec), BWMGT(token_buf_refill), BWMGT(token_buf_helpers), + + BWMGT(dir_conn_global_write_low), END_OF_TESTCASES }; diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c index 5d012e462b..885246628e 100644 --- a/src/test/test_channelpadding.c +++ b/src/test/test_channelpadding.c @@ -289,8 +289,6 @@ test_channelpadding_timers(void *arg) channel_t *chans[CHANNELS_TO_TEST]; (void)arg; - tor_libevent_postfork(); - if (!connection_array) connection_array = smartlist_new(); @@ -393,7 +391,6 @@ test_channelpadding_killonehop(void *arg) channelpadding_decision_t decision; int64_t new_time; (void)arg; - tor_libevent_postfork(); routerstatus_t *relay = tor_malloc_zero(sizeof(routerstatus_t)); monotime_init(); @@ -502,8 +499,6 @@ test_channelpadding_consensus(void *arg) int64_t new_time; (void)arg; - tor_libevent_postfork(); - /* * Params tested: * nf_pad_before_usage @@ -898,8 +893,6 @@ test_channelpadding_decide_to_pad_channel(void *arg) connection_array = smartlist_new(); (void)arg; - tor_libevent_postfork(); - monotime_init(); monotime_enable_test_mocking(); monotime_set_mock_time_nsec(1); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 0c23091594..7291e04d6a 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -167,6 +167,7 @@ test_upgrade_from_guard_wait(void *arg) tt_assert(!list); done: + smartlist_free(list); circuit_free(circ); entry_guard_free_(guard); } @@ -177,6 +178,6 @@ struct testcase_t circuitbuild_tests[] = { { "unsafe_exit", test_new_route_len_unsafe_exit, 0, NULL, NULL }, { "unhandled_exit", test_new_route_len_unhandled_exit, 0, NULL, NULL }, { "upgrade_from_guard_wait", test_upgrade_from_guard_wait, TT_FORK, - NULL, NULL }, + &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c index 25f8fd311b..934ddb0208 100644 --- a/src/test/test_circuitpadding.c +++ b/src/test/test_circuitpadding.c @@ -4,9 +4,11 @@ #define CIRCUITPADDING_MACHINES_PRIVATE #define NETWORKSTATUS_PRIVATE #define CRYPT_PATH_PRIVATE +#define RELAY_PRIVATE #include "core/or/or.h" #include "test/test.h" +#include "test/log_test_helpers.h" #include "lib/testsupport/testsupport.h" #include "core/or/connection_or.h" #include "core/or/channel.h" @@ -3152,6 +3154,29 @@ test_circuitpadding_hs_machines(void *arg) UNMOCK(circpad_machine_schedule_padding); } +/** Test that we effectively ignore non-padding cells in padding circuits. */ +static void +test_circuitpadding_ignore_non_padding_cells(void *arg) +{ + int retval; + relay_header_t rh; + + (void) arg; + + client_side = (circuit_t *)origin_circuit_new(); + client_side->purpose = CIRCUIT_PURPOSE_C_CIRCUIT_PADDING; + + rh.command = RELAY_COMMAND_BEGIN; + + setup_full_capture_of_logs(LOG_INFO); + retval = handle_relay_cell_command(NULL, client_side, NULL, NULL, &rh, 0); + tt_int_op(retval, OP_EQ, 0); + expect_log_msg_containing("Ignored cell"); + + done: + ; +} + #define TEST_CIRCUITPADDING(name, flags) \ { #name, test_##name, (flags), NULL, NULL } @@ -3175,5 +3200,6 @@ struct testcase_t circuitpadding_tests[] = { TEST_CIRCUITPADDING(circuitpadding_token_removal_exact, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_manage_circuit_lifetime, TT_FORK), TEST_CIRCUITPADDING(circuitpadding_hs_machines, TT_FORK), + TEST_CIRCUITPADDING(circuitpadding_ignore_non_padding_cells, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c index 2a09622f09..9bfaabeb2f 100644 --- a/src/test/test_circuitstats.c +++ b/src/test/test_circuitstats.c @@ -197,7 +197,7 @@ test_circuitstats_hoplen(void *arg) } #define TEST_CIRCUITSTATS(name, flags) \ - { #name, test_##name, (flags), NULL, NULL } + { #name, test_##name, (flags), &helper_pubsub_setup, NULL } struct testcase_t circuitstats_tests[] = { TEST_CIRCUITSTATS(circuitstats_hoplen, TT_FORK), diff --git a/src/test/test_cmdline.sh b/src/test/test_cmdline.sh new file mode 100755 index 0000000000..cf758c3851 --- /dev/null +++ b/src/test/test_cmdline.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +umask 077 +set -e + +if [ $# -ge 1 ]; then + TOR_BINARY="${1}" + shift +else + TOR_BINARY="${TESTING_TOR_BINARY:-./src/app/tor}" +fi + +echo "TOR BINARY IS ${TOR_BINARY}" + +die() { echo "$1" >&2 ; exit 5; } + +echo "A" + +DATA_DIR=$(mktemp -d -t tor_cmdline_tests.XXXXXX) +trap 'rm -rf "$DATA_DIR"' 0 + +# 1. Test list-torrc-options. +OUT="${DATA_DIR}/output" + +echo "B" +"${TOR_BINARY}" --list-torrc-options > "$OUT" + +echo "C" + +# regular options are given. +grep -i "SocksPort" "$OUT" >/dev/null || die "Did not find SocksPort" + + +echo "D" + +# unlisted options are given, since they do not have the NOSET flag. +grep -i "__SocksPort" "$OUT" > /dev/null || die "Did not find __SocksPort" + +echo "E" + +# unsettable options are not given. +if grep -i "DisableIOCP" "$OUT" /dev/null; then + die "Found DisableIOCP" +fi +if grep -i "HiddenServiceOptions" "$OUT" /dev/null ; then + die "Found HiddenServiceOptions" +fi +echo "OK" diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index 5d625483da..ecd97e3474 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -151,8 +151,6 @@ test_compat_libevent_postloop_events(void *arg) mainloop_event_t *a = NULL, *b = NULL; periodic_timer_t *timed = NULL; - tor_libevent_postfork(); - /* If postloop events don't work, then these events will activate one * another ad infinitum and, and the periodic event will never occur. */ b = mainloop_event_postloop_new(activate_event_cb, &a); diff --git a/src/test/test_config.c b/src/test/test_config.c index 3d49086713..ebc0624fb2 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -16,7 +16,7 @@ #include "core/or/circuitmux_ewma.h" #include "core/or/circuitbuild.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" #include "core/mainloop/connection.h" #include "core/or/connection_edge.h" #include "test/test.h" @@ -45,6 +45,7 @@ #include "app/config/statefile.h" #include "test/test_helpers.h" +#include "test/resolve_test_helpers.h" #include "feature/dirclient/dir_server_st.h" #include "core/or/port_cfg_st.h" @@ -1755,6 +1756,18 @@ add_default_fallback_dir_servers_known_default(void) n_add_default_fallback_dir_servers_known_default++; } +/* Helper for test_config_adding_dir_servers(), which should be + * refactored: clear the fields in the options which the options object + * does not really own. */ +static void +ads_clear_helper(or_options_t *options) +{ + options->DirAuthorities = NULL; + options->AlternateBridgeAuthority = NULL; + options->AlternateDirAuthority = NULL; + options->FallbackDir = NULL; +} + /* Test all the different combinations of adding dir servers */ static void test_config_adding_dir_servers(void *arg) @@ -1762,7 +1775,7 @@ test_config_adding_dir_servers(void *arg) (void)arg; /* allocate options */ - or_options_t *options = tor_malloc_zero(sizeof(or_options_t)); + or_options_t *options = options_new(); /* Allocate and populate configuration lines: * @@ -1885,7 +1898,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -1967,7 +1982,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -2108,7 +2125,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -2249,7 +2268,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -2391,7 +2412,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -2543,7 +2566,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -2697,7 +2722,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -2860,7 +2887,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -3017,7 +3046,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -3183,7 +3214,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -3346,7 +3379,9 @@ test_config_adding_dir_servers(void *arg) n_add_default_fallback_dir_servers_known_default = 0; /* clear options*/ - memset(options, 0, sizeof(or_options_t)); + ads_clear_helper(options); + or_options_free(options); + options = options_new(); /* clear any previous dir servers: consider_adding_dir_servers() should do this anyway */ @@ -3515,10 +3550,7 @@ test_config_adding_dir_servers(void *arg) tor_free(test_fallback_directory->value); tor_free(test_fallback_directory); - options->DirAuthorities = NULL; - options->AlternateBridgeAuthority = NULL; - options->AlternateDirAuthority = NULL; - options->FallbackDir = NULL; + ads_clear_helper(options); or_options_free(options); UNMOCK(add_default_fallback_dir_servers); @@ -3533,7 +3565,7 @@ test_config_default_dir_servers(void *arg) int fallback_count = 0; /* new set of options should stop fallback parsing */ - opts = tor_malloc_zero(sizeof(or_options_t)); + opts = options_new(); opts->UseDefaultFallbackDirs = 0; /* set old_options to NULL to force dir update */ consider_adding_dir_servers(opts, NULL); @@ -3547,7 +3579,7 @@ test_config_default_dir_servers(void *arg) /* if we disable the default fallbacks, there must not be any extra */ tt_assert(fallback_count == trusted_count); - opts = tor_malloc_zero(sizeof(or_options_t)); + opts = options_new(); opts->UseDefaultFallbackDirs = 1; consider_adding_dir_servers(opts, opts); trusted_count = smartlist_len(router_get_trusted_dir_servers()); @@ -3607,7 +3639,7 @@ test_config_directory_fetch(void *arg) (void)arg; /* Test Setup */ - or_options_t *options = tor_malloc_zero(sizeof(or_options_t)); + or_options_t *options = options_new(); routerinfo_t routerinfo; memset(&routerinfo, 0, sizeof(routerinfo)); mock_router_pick_published_address_result = -1; @@ -3619,9 +3651,10 @@ test_config_directory_fetch(void *arg) mock_router_my_exit_policy_is_reject_star); MOCK(advertised_server_mode, mock_advertised_server_mode); MOCK(router_get_my_routerinfo, mock_router_get_my_routerinfo); + or_options_free(options); + options = options_new(); /* Clients can use multiple directory mirrors for bootstrap */ - memset(options, 0, sizeof(or_options_t)); options->ClientOnly = 1; tt_assert(server_mode(options) == 0); tt_assert(public_server_mode(options) == 0); @@ -3630,7 +3663,8 @@ test_config_directory_fetch(void *arg) OP_EQ, 1); /* Bridge Clients can use multiple directory mirrors for bootstrap */ - memset(options, 0, sizeof(or_options_t)); + or_options_free(options); + options = options_new(); options->UseBridges = 1; tt_assert(server_mode(options) == 0); tt_assert(public_server_mode(options) == 0); @@ -3640,7 +3674,8 @@ test_config_directory_fetch(void *arg) /* Bridge Relays (Bridges) must act like clients, and use multiple * directory mirrors for bootstrap */ - memset(options, 0, sizeof(or_options_t)); + or_options_free(options); + options = options_new(); options->BridgeRelay = 1; options->ORPort_set = 1; tt_assert(server_mode(options) == 1); @@ -3651,7 +3686,8 @@ test_config_directory_fetch(void *arg) /* Clients set to FetchDirInfoEarly must fetch it from the authorities, * but can use multiple authorities for bootstrap */ - memset(options, 0, sizeof(or_options_t)); + or_options_free(options); + options = options_new(); options->FetchDirInfoEarly = 1; tt_assert(server_mode(options) == 0); tt_assert(public_server_mode(options) == 0); @@ -3662,7 +3698,8 @@ test_config_directory_fetch(void *arg) /* OR servers only fetch the consensus from the authorities when they don't * know their own address, but never use multiple directories for bootstrap */ - memset(options, 0, sizeof(or_options_t)); + or_options_free(options); + options = options_new(); options->ORPort_set = 1; mock_router_pick_published_address_result = -1; @@ -3682,7 +3719,8 @@ test_config_directory_fetch(void *arg) /* Exit OR servers only fetch the consensus from the authorities when they * refuse unknown exits, but never use multiple directories for bootstrap */ - memset(options, 0, sizeof(or_options_t)); + or_options_free(options); + options = options_new(); options->ORPort_set = 1; options->ExitRelay = 1; mock_router_pick_published_address_result = 0; @@ -3712,7 +3750,8 @@ test_config_directory_fetch(void *arg) * advertising their dirport, and never use multiple directories for * bootstrap. This only applies if they are also OR servers. * (We don't care much about the behaviour of non-OR directory servers.) */ - memset(options, 0, sizeof(or_options_t)); + or_options_free(options); + options = options_new(); options->DirPort_set = 1; options->ORPort_set = 1; options->DirCache = 1; @@ -3766,7 +3805,7 @@ test_config_directory_fetch(void *arg) OP_EQ, 0); done: - tor_free(options); + or_options_free(options); UNMOCK(router_pick_published_address); UNMOCK(router_get_my_routerinfo); UNMOCK(advertised_server_mode); @@ -4030,6 +4069,8 @@ test_config_parse_port_config__ports__ports_given(void *data) slout = smartlist_new(); + mock_hostname_resolver(); + // Test error when encounters an invalid Port specification config_port_invalid = mock_config_line("DNSPort", ""); ret = parse_port_config(NULL, config_port_invalid, "DNS", 0, NULL, @@ -4726,6 +4767,7 @@ test_config_parse_port_config__ports__ports_given(void *data) #endif /* defined(_WIN32) */ done: + unmock_hostname_resolver(); if (slout) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_free(slout); @@ -6028,6 +6070,31 @@ test_config_kvline_parse(void *arg) tor_free(enc); } +static void +test_config_getinfo_config_names(void *arg) +{ + (void)arg; + char *answer = NULL; + const char *error = NULL; + int rv; + + rv = getinfo_helper_config(NULL, "config/names", &answer, &error); + tt_int_op(rv, OP_EQ, 0); + tt_ptr_op(error, OP_EQ, NULL); + + // ContactInfo should be listed. + tt_assert(strstr(answer, "\nContactInfo String\n")); + + // V1AuthoritativeDirectory should not be listed, since it is obsolete. + tt_assert(! strstr(answer, "V1AuthoritativeDirectory")); + + // ___UsingTestNetworkDefaults should not be listed, since it is invisible. + tt_assert(! strstr(answer, "UsingTestNetworkDefaults")); + + done: + tor_free(answer); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } @@ -6081,5 +6148,6 @@ struct testcase_t config_tests[] = { CONFIG_TEST(compute_max_mem_in_queues, 0), CONFIG_TEST(extended_fmt, 0), CONFIG_TEST(kvline_parse, 0), + CONFIG_TEST(getinfo_config_names, 0), END_OF_TESTCASES }; diff --git a/src/test/test_confmgr.c b/src/test/test_confmgr.c new file mode 100644 index 0000000000..d5c73b48e4 --- /dev/null +++ b/src/test/test_confmgr.c @@ -0,0 +1,325 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * Tests for confparse.c's features that support multiple configuration + * formats and configuration objects. + */ + +#define CONFPARSE_PRIVATE +#include "orconfig.h" + +#include "core/or/or.h" +#include "lib/encoding/confline.h" +#include "lib/confmgt/confparse.h" +#include "test/test.h" +#include "test/log_test_helpers.h" + +/* + * Set up a few objects: a pasture_cfg is toplevel; it has a llama_cfg and an + * alpaca_cfg. + */ + +typedef struct { + uint32_t magic; + char *address; + int opentopublic; + config_suite_t *subobjs; +} pasture_cfg_t; + +typedef struct { + char *llamaname; + int cuteness; + uint32_t magic; + int eats_meat; /* deprecated; llamas are never carnivorous. */ + + char *description; // derived from other fields. +} llama_cfg_t; + +typedef struct { + uint32_t magic; + int fuzziness; + char *alpacaname; + int n_wings; /* deprecated; alpacas don't have wings. */ +} alpaca_cfg_t; + +/* + * Make the above into configuration objects. + */ + +static pasture_cfg_t pasture_cfg_t_dummy; +static llama_cfg_t llama_cfg_t_dummy; +static alpaca_cfg_t alpaca_cfg_t_dummy; + +#define PV(name, type, dflt) \ + CONFIG_VAR_ETYPE(pasture_cfg_t, #name, type, name, 0, dflt) +#define LV(name, type, dflt) \ + CONFIG_VAR_ETYPE(llama_cfg_t, #name, type, name, 0, dflt) +#define AV(name, type, dflt) \ + CONFIG_VAR_ETYPE(alpaca_cfg_t, #name, type, name, 0, dflt) +static const config_var_t pasture_vars[] = { + PV(address, STRING, NULL), + PV(opentopublic, BOOL, "1"), + END_OF_CONFIG_VARS +}; +static const config_var_t llama_vars[] = +{ + LV(llamaname, STRING, NULL), + LV(eats_meat, BOOL, NULL), + LV(cuteness, POSINT, "100"), + END_OF_CONFIG_VARS +}; +static const config_var_t alpaca_vars[] = +{ + AV(alpacaname, STRING, NULL), + AV(fuzziness, POSINT, "50"), + AV(n_wings, POSINT, "0"), + END_OF_CONFIG_VARS +}; + +static config_deprecation_t llama_deprecations[] = { + { "eats_meat", "Llamas are herbivores." }, + {NULL,NULL} +}; + +static config_deprecation_t alpaca_deprecations[] = { + { "n_wings", "Alpacas are quadrupeds." }, + {NULL,NULL} +}; + +static int clear_llama_cfg_called = 0; +static void +clear_llama_cfg(const config_mgr_t *mgr, void *llamacfg) +{ + (void)mgr; + llama_cfg_t *lc = llamacfg; + tor_free(lc->description); + ++clear_llama_cfg_called; +} + +static config_abbrev_t llama_abbrevs[] = { + { "gracia", "cuteness", 0, 0 }, + { "gentillesse", "cuteness", 0, 0 }, + { NULL, NULL, 0, 0 }, +}; + +static const config_format_t pasture_fmt = { + sizeof(pasture_cfg_t), + { + "pasture_cfg_t", + 8989, + offsetof(pasture_cfg_t, magic) + }, + .vars = pasture_vars, + .config_suite_offset = offsetof(pasture_cfg_t, subobjs), +}; + +static const config_format_t llama_fmt = { + sizeof(llama_cfg_t), + { + "llama_cfg_t", + 0x11aa11, + offsetof(llama_cfg_t, magic) + }, + .vars = llama_vars, + .config_suite_offset = -1, + .deprecations = llama_deprecations, + .abbrevs = llama_abbrevs, + .clear_fn = clear_llama_cfg, +}; + +static const config_format_t alpaca_fmt = { + sizeof(alpaca_cfg_t), + { + "alpaca_cfg_t", + 0xa15aca, + offsetof(alpaca_cfg_t, magic) + }, + .vars = alpaca_vars, + .config_suite_offset = -1, + .deprecations = alpaca_deprecations, +}; + +#define LLAMA_IDX 0 +#define ALPACA_IDX 1 + +static config_mgr_t * +get_mgr(bool freeze) +{ + config_mgr_t *mgr = config_mgr_new(&pasture_fmt); + tt_int_op(LLAMA_IDX, OP_EQ, config_mgr_add_format(mgr, &llama_fmt)); + tt_int_op(ALPACA_IDX, OP_EQ, config_mgr_add_format(mgr, &alpaca_fmt)); + if (freeze) + config_mgr_freeze(mgr); + return mgr; + + done: + config_mgr_free(mgr); + return NULL; +} + +static void +test_confmgr_init(void *arg) +{ + (void)arg; + config_mgr_t *mgr = get_mgr(true); + smartlist_t *vars = NULL; + tt_ptr_op(mgr, OP_NE, NULL); + + vars = config_mgr_list_vars(mgr); + tt_int_op(smartlist_len(vars), OP_EQ, 8); // 8 vars total. + + tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "CUTENESS")); + tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "GRACIA")); + smartlist_free(vars); + + vars = config_mgr_list_deprecated_vars(mgr); // 2 deprecated vars. + tt_int_op(smartlist_len(vars), OP_EQ, 2); + tt_assert(smartlist_contains_string(vars, "eats_meat")); + tt_assert(smartlist_contains_string(vars, "n_wings")); + + tt_str_op("Llamas are herbivores.", OP_EQ, + config_find_deprecation(mgr, "EATS_MEAT")); + tt_str_op("Alpacas are quadrupeds.", OP_EQ, + config_find_deprecation(mgr, "N_WINGS")); + + done: + smartlist_free(vars); + config_mgr_free(mgr); +} + +static void +test_confmgr_magic(void *args) +{ + (void)args; + // Every time we build a manager, it is supposed to get a different magic + // number. Let's test that. + config_mgr_t *mgr1 = get_mgr(true); + config_mgr_t *mgr2 = get_mgr(true); + config_mgr_t *mgr3 = get_mgr(true); + + pasture_cfg_t *p1 = NULL, *p2 = NULL, *p3 = NULL; + + tt_assert(mgr1); + tt_assert(mgr2); + tt_assert(mgr3); + + p1 = config_new(mgr1); + p2 = config_new(mgr2); + p3 = config_new(mgr3); + + tt_assert(p1); + tt_assert(p2); + tt_assert(p3); + + // By chance, two managers get the same magic with P=2^-32. Let's + // make sure that at least two of them are different, so that our + // odds of a false positive are 1/2^-64. + tt_assert((p1->magic != p2->magic) || (p2->magic != p3->magic)); + + done: + config_free(mgr1, p1); + config_free(mgr2, p2); + config_free(mgr3, p3); + + config_mgr_free(mgr1); + config_mgr_free(mgr2); + config_mgr_free(mgr3); +} + +static const char *simple_pasture = + "LLamaname hugo\n" + "Alpacaname daphne\n" + "gentillesse 42\n" + "address 123 Camelid ave\n"; + +static void +test_confmgr_parse(void *arg) +{ + (void)arg; + config_mgr_t *mgr = get_mgr(true); + pasture_cfg_t *p = config_new(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, p); // set defaults. + + int r = config_get_lines(simple_pasture, &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, p, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + + tt_int_op(p->opentopublic, OP_EQ, 1); + tt_str_op(p->address, OP_EQ, "123 Camelid ave"); + + // We are using this API directly; modules outside confparse will, in the + // future, not. + const alpaca_cfg_t *ac = config_mgr_get_obj(mgr, p, ALPACA_IDX); + const llama_cfg_t *lc = config_mgr_get_obj(mgr, p, LLAMA_IDX); + tt_str_op(lc->llamaname, OP_EQ, "hugo"); + tt_str_op(ac->alpacaname, OP_EQ, "daphne"); + tt_int_op(lc->cuteness, OP_EQ, 42); + tt_int_op(ac->fuzziness, OP_EQ, 50); + + // We set the description for the llama here, so that the clear function + // can clear it. (Later we can do this in a verification function.) + clear_llama_cfg_called = 0; + llama_cfg_t *mut_lc = config_mgr_get_obj_mutable(mgr, p, LLAMA_IDX); + mut_lc->description = tor_strdup("A llama named Hugo."); + config_free(mgr, p); + tt_int_op(clear_llama_cfg_called, OP_EQ, 1); + + done: + config_free_lines(lines); + config_free(mgr, p); + config_mgr_free(mgr); + tor_free(msg); +} + +static void +test_confmgr_dump(void *arg) +{ + (void)arg; + config_mgr_t *mgr = get_mgr(true); + pasture_cfg_t *p = config_new(mgr); + pasture_cfg_t *defaults = config_new(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + char *s = NULL; + + config_init(mgr, p); // set defaults. + config_init(mgr, defaults); // set defaults. + + int r = config_get_lines(simple_pasture, &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, p, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + + s = config_dump(mgr, defaults, p, 1, 0); + tt_str_op("address 123 Camelid ave\n" + "alpacaname daphne\n" + "cuteness 42\n" + "llamaname hugo\n", OP_EQ, s); + + done: + config_free_lines(lines); + config_free(mgr, p); + config_free(mgr, defaults); + config_mgr_free(mgr); + + tor_free(msg); + tor_free(s); +} + +#define CONFMGR_TEST(name, flags) \ + { #name, test_confmgr_ ## name, flags, NULL, NULL } + +struct testcase_t confmgr_tests[] = { + CONFMGR_TEST(init, 0), + CONFMGR_TEST(magic, 0), + CONFMGR_TEST(parse, 0), + CONFMGR_TEST(dump, 0), + END_OF_TESTCASES +}; diff --git a/src/test/test_confparse.c b/src/test/test_confparse.c new file mode 100644 index 0000000000..5f29a22c10 --- /dev/null +++ b/src/test/test_confparse.c @@ -0,0 +1,1086 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* + * Tests for confparse.c module that we use to parse various + * configuration/state file types. + */ + +#define CONFPARSE_PRIVATE +#include "orconfig.h" + +#include "core/or/or.h" +#include "lib/encoding/confline.h" +#include "feature/nodelist/routerset.h" +#include "lib/confmgt/confparse.h" +#include "test/test.h" +#include "test/log_test_helpers.h" + +#include "lib/confmgt/unitparse.h" + +typedef struct test_struct_t { + uint32_t magic; + char *s; + char *fn; + int pos; + int i; + int deprecated_int; + uint64_t u64; + int interval; + int msec_interval; + uint64_t mem; + double dbl; + int boolean; + int autobool; + time_t time; + smartlist_t *csv; + int csv_interval; + config_line_t *lines; + config_line_t *mixed_lines; + routerset_t *routerset; + int hidden_int; + config_line_t *mixed_hidden_lines; + + config_line_t *extra_lines; +} test_struct_t; + +static test_struct_t test_struct_t_dummy; + +#define VAR(varname,conftype,member,initvalue) \ + CONFIG_VAR_ETYPE(test_struct_t, varname, conftype, member, 0, initvalue) +#define V(member,conftype,initvalue) \ + VAR(#member, conftype, member, initvalue) +#define OBSOLETE(varname) \ + CONFIG_VAR_OBSOLETE(varname) + +static const config_var_t test_vars[] = { + V(s, STRING, "hello"), + V(fn, FILENAME, NULL), + V(pos, POSINT, NULL), + V(i, INT, "-10"), + V(deprecated_int, INT, "3"), + V(u64, UINT64, NULL), + V(interval, INTERVAL, "10 seconds"), + V(msec_interval, MSEC_INTERVAL, "150 msec"), + V(mem, MEMUNIT, "10 MB"), + V(dbl, DOUBLE, NULL), + V(boolean, BOOL, "0"), + V(autobool, AUTOBOOL, "auto"), + V(time, ISOTIME, NULL), + V(csv, CSV, NULL), + V(csv_interval, CSV_INTERVAL, "5 seconds"), + V(lines, LINELIST, NULL), + VAR("MixedLines", LINELIST_V, mixed_lines, NULL), + VAR("LineTypeA", LINELIST_S, mixed_lines, NULL), + VAR("LineTypeB", LINELIST_S, mixed_lines, NULL), + OBSOLETE("obsolete"), + { + .member = { .name = "routerset", + .type = CONFIG_TYPE_EXTENDED, + .type_def = &ROUTERSET_type_defn, + .offset = offsetof(test_struct_t, routerset), + }, + }, + VAR("__HiddenInt", POSINT, hidden_int, "0"), + VAR("MixedHiddenLines", LINELIST_V, mixed_hidden_lines, NULL), + VAR("__HiddenLineA", LINELIST_S, mixed_hidden_lines, NULL), + VAR("VisibleLineB", LINELIST_S, mixed_hidden_lines, NULL), + + END_OF_CONFIG_VARS, +}; + +static config_abbrev_t test_abbrevs[] = { + { "uint", "pos", 0, 0 }, + { "float", "dbl", 0, 1 }, + { NULL, NULL, 0, 0 } +}; + +static config_deprecation_t test_deprecation_notes[] = { + { "deprecated_int", "This integer is deprecated." }, + { NULL, NULL } +}; + +static int +test_validate_cb(void *old_options, void *options, void *default_options, + int from_setconf, char **msg) +{ + (void)old_options; + (void)default_options; + (void)from_setconf; + (void)msg; + test_struct_t *ts = options; + + if (ts->i == 0xbad) { + *msg = tor_strdup("bad value for i"); + return -1; + } + return 0; +} + +#define TEST_MAGIC 0x1337 + +static const config_format_t test_fmt = { + sizeof(test_struct_t), + { + "test_struct_t", + TEST_MAGIC, + offsetof(test_struct_t, magic), + }, + test_abbrevs, + test_deprecation_notes, + test_vars, + test_validate_cb, + NULL, + NULL, + -1, +}; + +/* Make sure that config_init sets everything to the right defaults. */ +static void +test_confparse_init(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = config_new(mgr); + config_init(mgr, tst); + + // Make sure that options are initialized right. */ + tt_str_op(tst->s, OP_EQ, "hello"); + tt_ptr_op(tst->fn, OP_EQ, NULL); + tt_int_op(tst->pos, OP_EQ, 0); + tt_int_op(tst->i, OP_EQ, -10); + tt_int_op(tst->deprecated_int, OP_EQ, 3); + tt_u64_op(tst->u64, OP_EQ, 0); + tt_int_op(tst->interval, OP_EQ, 10); + tt_int_op(tst->msec_interval, OP_EQ, 150); + tt_u64_op(tst->mem, OP_EQ, 10 * 1024 * 1024); + tt_double_op(tst->dbl, OP_LT, .0000000001); + tt_double_op(tst->dbl, OP_GT, -0.0000000001); + tt_int_op(tst->boolean, OP_EQ, 0); + tt_int_op(tst->autobool, OP_EQ, -1); + tt_i64_op(tst->time, OP_EQ, 0); + tt_ptr_op(tst->csv, OP_EQ, NULL); + tt_int_op(tst->csv_interval, OP_EQ, 5); + tt_ptr_op(tst->lines, OP_EQ, NULL); + tt_ptr_op(tst->mixed_lines, OP_EQ, NULL); + tt_int_op(tst->hidden_int, OP_EQ, 0); + + done: + config_free(mgr, tst); + config_mgr_free(mgr); +} + +static const char simple_settings[] = + "s this is a \n" + "fn /simple/test of the\n" + "uint 77\n" // this is an abbrev + "i 3\n" + "u64 1000000000000 \n" + "interval 5 minutes \n" + "msec_interval 5 minutes \n" + "mem 10\n" + "dbl 6.060842\n" + "BOOLEAN 1\n" + "aUtObOOl 0\n" + "time 2019-06-14 13:58:51\n" + "csv configuration, parsing , system \n" + "csv_interval 10 seconds, 5 seconds, 10 hours\n" + "lines hello\n" + "LINES world\n" + "linetypea i d\n" + "linetypeb i c\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "__hiddenint 11\n" + "__hiddenlineA XYZ\n" + "visiblelineB ABC\n"; + +/* Return a configuration object set up from simple_settings above. */ +static test_struct_t * +get_simple_config(const config_mgr_t *mgr) +{ + test_struct_t *result = NULL; + test_struct_t *tst = config_new(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines(simple_settings, &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + result = tst; + tst = NULL; // prevent free + done: + tor_free(msg); + config_free_lines(lines); + config_free(mgr, tst); + return result; +} + +/* Make sure that config_assign can parse things. */ +static void +test_confparse_assign_simple(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + + tt_str_op(tst->s, OP_EQ, "this is a"); + tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); + tt_int_op(tst->pos, OP_EQ, 77); + tt_int_op(tst->i, OP_EQ, 3); + tt_int_op(tst->deprecated_int, OP_EQ, 3); + tt_u64_op(tst->u64, OP_EQ, UINT64_C(1000000000000)); + tt_int_op(tst->interval, OP_EQ, 5 * 60); + tt_int_op(tst->msec_interval, OP_EQ, 5 * 60 * 1000); + tt_u64_op(tst->mem, OP_EQ, 10); + tt_double_op(tst->dbl, OP_LT, 6.060843); + tt_double_op(tst->dbl, OP_GT, 6.060841); + tt_int_op(tst->boolean, OP_EQ, 1); + tt_int_op(tst->autobool, OP_EQ, 0); + tt_i64_op(tst->time, OP_EQ, 1560520731); + tt_ptr_op(tst->csv, OP_NE, NULL); + tt_int_op(smartlist_len(tst->csv), OP_EQ, 3); + tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "configuration"); + tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "parsing"); + tt_str_op(smartlist_get(tst->csv, 2), OP_EQ, "system"); + tt_int_op(tst->csv_interval, OP_EQ, 10); + tt_int_op(tst->hidden_int, OP_EQ, 11); + + tt_assert(tst->lines); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "hello"); + tt_assert(tst->lines->next); + tt_str_op(tst->lines->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->value, OP_EQ, "world"); + tt_assert(!tst->lines->next->next); + + tt_assert(tst->mixed_lines); + tt_str_op(tst->mixed_lines->key, OP_EQ, "LineTypeA"); + tt_str_op(tst->mixed_lines->value, OP_EQ, "i d"); + tt_assert(tst->mixed_lines->next); + tt_str_op(tst->mixed_lines->next->key, OP_EQ, "LineTypeB"); + tt_str_op(tst->mixed_lines->next->value, OP_EQ, "i c"); + tt_assert(!tst->mixed_lines->next->next); + + tt_assert(tst->mixed_hidden_lines); + tt_str_op(tst->mixed_hidden_lines->key, OP_EQ, "__HiddenLineA"); + tt_str_op(tst->mixed_hidden_lines->value, OP_EQ, "XYZ"); + tt_assert(tst->mixed_hidden_lines->next); + tt_str_op(tst->mixed_hidden_lines->next->key, OP_EQ, "VisibleLineB"); + tt_str_op(tst->mixed_hidden_lines->next->value, OP_EQ, "ABC"); + tt_assert(!tst->mixed_hidden_lines->next->next); + + tt_assert(config_check_ok(mgr, tst, LOG_ERR)); + + done: + config_free(mgr, tst); + config_mgr_free(mgr); +} + +/* Try to assign to an obsolete option, and make sure we get a warning. */ +static void +test_confparse_assign_obsolete(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines("obsolete option here", + &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("Skipping obsolete configuration option"); + + done: + teardown_capture_of_logs(); + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +/* Try to assign to an deprecated option, and make sure we get a warning + * but the assignment works anyway. */ +static void +test_confparse_assign_deprecated(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines("deprecated_int 7", + &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("This integer is deprecated."); + + tt_int_op(tst->deprecated_int, OP_EQ, 7); + + tt_assert(config_check_ok(mgr, tst, LOG_ERR)); + + done: + teardown_capture_of_logs(); + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +/* Try to re-assign an option name that has been depreacted in favor of + * another. */ +static void +test_confparse_assign_replaced(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines("float 1000\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(mgr, tst, lines, CAL_WARN_DEPRECATIONS, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("use 'dbl' instead."); + + tt_double_op(tst->dbl, OP_GT, 999.999); + tt_double_op(tst->dbl, OP_LT, 1000.001); + + done: + teardown_capture_of_logs(); + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +/* Try to set a linelist value with no option. */ +static void +test_confparse_assign_emptystring(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines("lines\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("has no value"); + + done: + teardown_capture_of_logs(); + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +/* Try to set a the same option twice; make sure we get a warning. */ +static void +test_confparse_assign_twice(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines("pos 10\n" + "pos 99\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + expect_single_log_msg_containing("used more than once"); + + done: + teardown_capture_of_logs(); + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +typedef struct badval_test_t { + const char *cfg; + const char *expect_msg; +} badval_test_t; + +/* Try to set an option and make sure that we get a failure and an expected + * warning. */ +static void +test_confparse_assign_badval(void *arg) +{ + const badval_test_t *bt = arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + config_init(mgr, tst); + + int r = config_get_lines(bt->cfg, &lines, 0); + tt_int_op(r, OP_EQ, 0); + setup_capture_of_logs(LOG_WARN); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + if (! strstr(msg, bt->expect_msg)) { + TT_DIE(("'%s' did not contain '%s'" , msg, bt->expect_msg)); + } + + done: + teardown_capture_of_logs(); + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +/* Various arguments for badval test. + * + * Note that the expected warnings here are _very_ truncated, since we + * are writing these tests before a refactoring that we expect will + * change them. + */ +static const badval_test_t bv_notint = { "pos X\n", "malformed" }; +static const badval_test_t bv_negint = { "pos -10\n", "out of bounds" }; +static const badval_test_t bv_badu64 = { "u64 u64\n", "malformed" }; +static const badval_test_t bv_dbl1 = { "dbl xxx\n", "Could not convert" }; +static const badval_test_t bv_dbl2 = { "dbl 1.0 xx\n", "Could not convert" }; +static const badval_test_t bv_dbl3 = { + "dbl 1e-10000\n", "too small to express" }; +static const badval_test_t bv_dbl4 = { + "dbl 1e1000\n", "too large to express" }; +static const badval_test_t bv_dbl5 = { + "dbl -1e-10000\n", "too small to express" }; +static const badval_test_t bv_dbl6 = { + "dbl -1e1000\n", "too large to express" }; +static const badval_test_t bv_badcsvi1 = + { "csv_interval 10 wl\n", "malformed" }; +static const badval_test_t bv_badcsvi2 = + { "csv_interval cl,10\n", "malformed" }; +static const badval_test_t bv_nonoption = { "fnord 10\n", "Unknown option" }; +static const badval_test_t bv_badmem = { "mem 3 trits\n", "malformed" }; +static const badval_test_t bv_badbool = { "boolean 7\n", "Unrecognized value"}; +static const badval_test_t bv_badabool = + { "autobool 7\n", "Unrecognized value" }; +static const badval_test_t bv_badtime = { "time lunchtime\n", "Invalid time" }; +static const badval_test_t bv_virt = { "MixedLines 7\n", "virtual option" }; +static const badval_test_t bv_rs = { "Routerset 2.2.2.2.2\n", "Invalid" }; +static const badval_test_t bv_big_interval = + { "interval 1000 months", "too large" }; + +/* Try config_dump(), and make sure it behaves correctly */ +static void +test_confparse_dump(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + char *dumped = NULL; + + /* Minimal version. */ + dumped = config_dump(mgr, NULL, tst, 1, 0); + tt_str_op(dumped, OP_EQ, + "autobool 0\n" + "boolean 1\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "dbl 6.060842\n" + "fn /simple/test of the\n" + "i 3\n" + "interval 300\n" + "lines hello\n" + "lines world\n" + "mem 10\n" + "VisibleLineB ABC\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "msec_interval 300000\n" + "pos 77\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "s this is a\n" + "time 2019-06-14 13:58:51\n" + "u64 1000000000000\n"); + + tor_free(dumped); + dumped = config_dump(mgr, NULL, tst, 0, 0); + tt_str_op(dumped, OP_EQ, + "autobool 0\n" + "boolean 1\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "dbl 6.060842\n" + "deprecated_int 3\n" + "fn /simple/test of the\n" + "i 3\n" + "interval 300\n" + "lines hello\n" + "lines world\n" + "mem 10\n" + "VisibleLineB ABC\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "msec_interval 300000\n" + "pos 77\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "s this is a\n" + "time 2019-06-14 13:58:51\n" + "u64 1000000000000\n"); + + /* commented */ + tor_free(dumped); + dumped = config_dump(mgr, NULL, tst, 0, 1); + tt_str_op(dumped, OP_EQ, + "autobool 0\n" + "boolean 1\n" + "csv configuration,parsing,system\n" + "csv_interval 10\n" + "dbl 6.060842\n" + "# deprecated_int 3\n" + "fn /simple/test of the\n" + "i 3\n" + "interval 300\n" + "lines hello\n" + "lines world\n" + "mem 10\n" + "VisibleLineB ABC\n" + "LineTypeA i d\n" + "LineTypeB i c\n" + "msec_interval 300000\n" + "pos 77\n" + "routerset $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + "s this is a\n" + "time 2019-06-14 13:58:51\n" + "u64 1000000000000\n"); + + done: + config_free(mgr, tst); + tor_free(dumped); + config_mgr_free(mgr); +} + +/* Try confparse_reset_line(), and make sure it behaves correctly */ +static void +test_confparse_reset(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + + config_reset_line(mgr, tst, "interval", 0); + tt_int_op(tst->interval, OP_EQ, 0); + + config_reset_line(mgr, tst, "interval", 1); + tt_int_op(tst->interval, OP_EQ, 10); + + tt_ptr_op(tst->routerset, OP_NE, NULL); + config_reset_line(mgr, tst, "routerset", 0); + tt_ptr_op(tst->routerset, OP_EQ, NULL); + + done: + config_free(mgr, tst); + config_mgr_free(mgr); +} + +/* Try setting options a second time on a config object, and make sure + * it behaves correctly. */ +static void +test_confparse_reassign(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL, *rs = NULL; + + int r = config_get_lines( + "s eleven\n" + "i 12\n" + "lines 13\n" + "csv 14,15\n" + "routerset 127.0.0.1\n", + &lines, 0); + r = config_assign(mgr, tst,lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_str_op(tst->s, OP_EQ, "eleven"); + tt_str_op(tst->fn, OP_EQ, "/simple/test of the"); // unchanged + tt_int_op(tst->pos, OP_EQ, 77); // unchanged + tt_int_op(tst->i, OP_EQ, 12); + tt_ptr_op(tst->lines, OP_NE, NULL); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "13"); + tt_ptr_op(tst->lines->next, OP_EQ, NULL); + tt_int_op(smartlist_len(tst->csv), OP_EQ, 2); + tt_str_op(smartlist_get(tst->csv, 0), OP_EQ, "14"); + tt_str_op(smartlist_get(tst->csv, 1), OP_EQ, "15"); + + rs = routerset_to_string(tst->routerset); + tt_str_op(rs, OP_EQ, "127.0.0.1"); + + // Try again with the CLEAR_FIRST and USE_DEFAULTS flags + r = config_assign(mgr, tst, lines, + CAL_CLEAR_FIRST|CAL_USE_DEFAULTS, &msg); + tt_int_op(r, OP_EQ, 0); + + tt_ptr_op(msg, OP_EQ, NULL); + tt_str_op(tst->s, OP_EQ, "eleven"); + // tt_ptr_op(tst->fn, OP_EQ, NULL); //XXXX why is this not cleared? + // tt_int_op(tst->pos, OP_EQ, 0); //XXXX why is this not cleared? + tt_int_op(tst->i, OP_EQ, 12); + + done: + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + tor_free(rs); + config_mgr_free(mgr); +} + +/* Try setting options a second time on a config object, using the +foo + * linelist-extending syntax. */ +static void +test_confparse_reassign_extend(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + char *msg = NULL; + + int r = config_get_lines( + "+lines 13\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, tst,lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_assert(tst->lines); + tt_str_op(tst->lines->key, OP_EQ, "lines"); + tt_str_op(tst->lines->value, OP_EQ, "hello"); + tt_assert(tst->lines->next); + tt_str_op(tst->lines->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->value, OP_EQ, "world"); + tt_assert(tst->lines->next->next); + tt_str_op(tst->lines->next->next->key, OP_EQ, "lines"); + tt_str_op(tst->lines->next->next->value, OP_EQ, "13"); + tt_assert(tst->lines->next->next->next == NULL); + config_free_lines(lines); + + r = config_get_lines( + "/lines\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + tt_assert(tst->lines == NULL); + config_free_lines(lines); + + config_free(mgr, tst); + tst = get_simple_config(mgr); + r = config_get_lines( + "/lines away!\n", + &lines, 1); // allow extended format. + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + tt_assert(tst->lines == NULL); + + done: + config_free(mgr, tst); + config_free_lines(lines); + tor_free(msg); + config_mgr_free(mgr); +} + +/* Test out confparse_get_assigned(). */ +static void +test_confparse_get_assigned(void *arg) +{ + (void)arg; + + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = get_simple_config(mgr); + config_line_t *lines = NULL; + + lines = config_get_assigned_option(mgr, tst, "I", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "i"); + tt_str_op(lines->value, OP_EQ, "3"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(mgr, tst, "s", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "s"); + tt_str_op(lines->value, OP_EQ, "this is a"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(mgr, tst, "obsolete", 1); + tt_assert(!lines); + + lines = config_get_assigned_option(mgr, tst, "nonesuch", 1); + tt_assert(!lines); + + lines = config_get_assigned_option(mgr, tst, "mixedlines", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "LineTypeA"); + tt_str_op(lines->value, OP_EQ, "i d"); + tt_assert(lines->next); + tt_str_op(lines->next->key, OP_EQ, "LineTypeB"); + tt_str_op(lines->next->value, OP_EQ, "i c"); + tt_assert(lines->next->next == NULL); + config_free_lines(lines); + + lines = config_get_assigned_option(mgr, tst, "linetypeb", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "LineTypeB"); + tt_str_op(lines->value, OP_EQ, "i c"); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + tor_free(tst->s); + tst->s = tor_strdup("Hello\nWorld"); + lines = config_get_assigned_option(mgr, tst, "s", 1); + tt_assert(lines); + tt_str_op(lines->key, OP_EQ, "s"); + tt_str_op(lines->value, OP_EQ, "\"Hello\\nWorld\""); + tt_assert(lines->next == NULL); + config_free_lines(lines); + + done: + config_free(mgr, tst); + config_free_lines(lines); + config_mgr_free(mgr); +} + +/* Another variant, which accepts and stores unrecognized lines.*/ +#define ETEST_MAGIC 13371337 + +static struct_member_t extra = { + .name = "__extra", + .type = CONFIG_TYPE_LINELIST, + .offset = offsetof(test_struct_t, extra_lines), +}; + +static config_format_t etest_fmt = { + sizeof(test_struct_t), + { + "test_struct_t (with extra lines)", + ETEST_MAGIC, + offsetof(test_struct_t, magic), + }, + test_abbrevs, + test_deprecation_notes, + test_vars, + test_validate_cb, + NULL, + &extra, + -1, +}; + +/* Try out the feature where we can store unrecognized lines and dump them + * again. (State files use this.) */ +static void +test_confparse_extra_lines(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&etest_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = config_new(mgr); + config_line_t *lines = NULL; + char *msg = NULL, *dump = NULL; + + config_init(mgr, tst); + + int r = config_get_lines( + "unknotty addita\n" + "pos 99\n" + "wombat knish\n", &lines, 0); + tt_int_op(r, OP_EQ, 0); + r = config_assign(mgr, tst, lines, 0, &msg); + tt_int_op(r, OP_EQ, 0); + tt_ptr_op(msg, OP_EQ, NULL); + + tt_assert(tst->extra_lines); + + dump = config_dump(mgr, NULL, tst, 1, 0); + tt_str_op(dump, OP_EQ, + "pos 99\n" + "unknotty addita\n" + "wombat knish\n"); + + done: + tor_free(msg); + tor_free(dump); + config_free_lines(lines); + config_free(mgr, tst); + config_mgr_free(mgr); +} + +static void +test_confparse_unitparse(void *args) +{ + (void)args; + /* spot-check a few memunit values. */ + int ok = 3; + tt_u64_op(config_parse_memunit("100 MB", &ok), OP_EQ, 100<<20); + tt_assert(ok); + tt_u64_op(config_parse_memunit("100 TB", &ok), OP_EQ, UINT64_C(100)<<40); + tt_assert(ok); + // This is a floating-point value, but note that 1.5 can be represented + // precisely. + tt_u64_op(config_parse_memunit("1.5 MB", &ok), OP_EQ, 3<<19); + tt_assert(ok); + + /* Try some good intervals and msec intervals */ + tt_int_op(config_parse_interval("2 days", &ok), OP_EQ, 48*3600); + tt_assert(ok); + tt_int_op(config_parse_interval("1.5 hour", &ok), OP_EQ, 5400); + tt_assert(ok); + tt_u64_op(config_parse_interval("1 minute", &ok), OP_EQ, 60); + tt_assert(ok); + tt_int_op(config_parse_msec_interval("2 days", &ok), OP_EQ, 48*3600*1000); + tt_assert(ok); + tt_int_op(config_parse_msec_interval("10 msec", &ok), OP_EQ, 10); + tt_assert(ok); + + /* Try a couple of unitless values. */ + tt_int_op(config_parse_interval("10", &ok), OP_EQ, 10); + tt_assert(ok); + tt_u64_op(config_parse_interval("15.0", &ok), OP_EQ, 15); + tt_assert(ok); + + /* u64 overflow */ + /* XXXX our implementation does not currently detect this. See bug 30920. */ + /* + tt_u64_op(config_parse_memunit("20000000 TB", &ok), OP_EQ, 0); + tt_assert(!ok); + */ + + /* i32 overflow */ + tt_int_op(config_parse_interval("1000 months", &ok), OP_EQ, -1); + tt_assert(!ok); + tt_int_op(config_parse_msec_interval("4 weeks", &ok), OP_EQ, -1); + tt_assert(!ok); + + /* bad units */ + tt_u64_op(config_parse_memunit("7 nybbles", &ok), OP_EQ, 0); + tt_assert(!ok); + // XXXX these next two should return -1 according to the documentation. + tt_int_op(config_parse_interval("7 cowznofski", &ok), OP_EQ, 0); + tt_assert(!ok); + tt_int_op(config_parse_msec_interval("1 kalpa", &ok), OP_EQ, 0); + tt_assert(!ok); + + done: + ; +} + +static void +test_confparse_check_ok_fail(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + config_mgr_freeze(mgr); + test_struct_t *tst = config_new(mgr); + tst->pos = -10; + tt_assert(! config_check_ok(mgr, tst, LOG_INFO)); + + done: + config_free(mgr, tst); + config_mgr_free(mgr); +} + +static void +test_confparse_list_vars(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + smartlist_t *vars = config_mgr_list_vars(mgr); + smartlist_t *varnames = smartlist_new(); + char *joined = NULL; + + tt_assert(vars); + SMARTLIST_FOREACH(vars, config_var_t *, cv, + smartlist_add(varnames, (void*)cv->member.name)); + smartlist_sort_strings(varnames); + joined = smartlist_join_strings(varnames, "::", 0, NULL); + tt_str_op(joined, OP_EQ, + "LineTypeA::" + "LineTypeB::" + "MixedHiddenLines::" + "MixedLines::" + "VisibleLineB::" + "__HiddenInt::" + "__HiddenLineA::" + "autobool::" + "boolean::" + "csv::" + "csv_interval::" + "dbl::" + "deprecated_int::" + "fn::" + "i::" + "interval::" + "lines::" + "mem::" + "msec_interval::" + "obsolete::" + "pos::" + "routerset::" + "s::" + "time::" + "u64"); + + done: + tor_free(joined); + smartlist_free(varnames); + smartlist_free(vars); + config_mgr_free(mgr); +} + +static void +test_confparse_list_deprecated(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + smartlist_t *vars = config_mgr_list_deprecated_vars(mgr); + char *joined = NULL; + + tt_assert(vars); + smartlist_sort_strings(vars); + joined = smartlist_join_strings(vars, "::", 0, NULL); + + tt_str_op(joined, OP_EQ, "deprecated_int"); + + done: + tor_free(joined); + smartlist_free(vars); + config_mgr_free(mgr); +} + +static void +test_confparse_find_option_name(void *arg) +{ + (void)arg; + config_mgr_t *mgr = config_mgr_new(&test_fmt); + + // exact match + tt_str_op(config_find_option_name(mgr, "u64"), OP_EQ, "u64"); + // case-insensitive match + tt_str_op(config_find_option_name(mgr, "S"), OP_EQ, "s"); + tt_str_op(config_find_option_name(mgr, "linetypea"), OP_EQ, "LineTypeA"); + // prefix match + tt_str_op(config_find_option_name(mgr, "deprec"), OP_EQ, "deprecated_int"); + // explicit abbreviation + tt_str_op(config_find_option_name(mgr, "uint"), OP_EQ, "pos"); + tt_str_op(config_find_option_name(mgr, "UINT"), OP_EQ, "pos"); + // no match + tt_ptr_op(config_find_option_name(mgr, "absent"), OP_EQ, NULL); + + done: + config_mgr_free(mgr); +} + +#define CONFPARSE_TEST(name, flags) \ + { #name, test_confparse_ ## name, flags, NULL, NULL } + +#define BADVAL_TEST(name) \ + { "badval_" #name, test_confparse_assign_badval, 0, \ + &passthrough_setup, (void*)&bv_ ## name } + +struct testcase_t confparse_tests[] = { + CONFPARSE_TEST(init, 0), + CONFPARSE_TEST(assign_simple, 0), + CONFPARSE_TEST(assign_obsolete, 0), + CONFPARSE_TEST(assign_deprecated, 0), + CONFPARSE_TEST(assign_replaced, 0), + CONFPARSE_TEST(assign_emptystring, 0), + CONFPARSE_TEST(assign_twice, 0), + BADVAL_TEST(notint), + BADVAL_TEST(negint), + BADVAL_TEST(badu64), + BADVAL_TEST(dbl1), + BADVAL_TEST(dbl2), + BADVAL_TEST(dbl3), + BADVAL_TEST(dbl4), + BADVAL_TEST(dbl5), + BADVAL_TEST(dbl6), + BADVAL_TEST(badcsvi1), + BADVAL_TEST(badcsvi2), + BADVAL_TEST(nonoption), + BADVAL_TEST(badmem), + BADVAL_TEST(badbool), + BADVAL_TEST(badabool), + BADVAL_TEST(badtime), + BADVAL_TEST(virt), + BADVAL_TEST(rs), + BADVAL_TEST(big_interval), + CONFPARSE_TEST(dump, 0), + CONFPARSE_TEST(reset, 0), + CONFPARSE_TEST(reassign, 0), + CONFPARSE_TEST(reassign_extend, 0), + CONFPARSE_TEST(get_assigned, 0), + CONFPARSE_TEST(extra_lines, 0), + CONFPARSE_TEST(unitparse, 0), + CONFPARSE_TEST(check_ok_fail, 0), + CONFPARSE_TEST(list_vars, 0), + CONFPARSE_TEST(list_deprecated, 0), + CONFPARSE_TEST(find_option_name, 0), + END_OF_TESTCASES +}; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index ee48d656bd..55eb79e448 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -9,6 +9,7 @@ #include "feature/control/control.h" #include "feature/control/control_cmd.h" #include "feature/control/control_getinfo.h" +#include "feature/control/control_proto.h" #include "feature/client/entrynodes.h" #include "feature/hs/hs_common.h" #include "feature/nodelist/networkstatus.h" @@ -201,42 +202,72 @@ static const control_cmd_syntax_t one_arg_kwargs_syntax = { static const parse_test_params_t parse_one_arg_kwargs_params = TESTPARAMS( one_arg_kwargs_syntax, one_arg_kwargs_tests ); +static char *reply_str = NULL; +/* Mock for control_write_reply that copies the string for inspection + * by tests */ +static void +mock_control_write_reply(control_connection_t *conn, int code, int c, + const char *s) +{ + (void)conn; + (void)code; + (void)c; + tor_free(reply_str); + reply_str = tor_strdup(s); +} + static void test_add_onion_helper_keyarg_v3(void *arg) { int ret, hs_version; add_onion_secret_key_t pk; char *key_new_blob = NULL; - char *err_msg = NULL; const char *key_new_alg = NULL; (void) arg; + MOCK(control_write_reply, mock_control_write_reply); memset(&pk, 0, sizeof(pk)); /* Test explicit ED25519-V3 key generation. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); tt_assert(pk.v3); tt_str_op(key_new_alg, OP_EQ, "ED25519-V3"); tt_assert(key_new_blob); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); + /* Test "BEST" key generation (Assumes BEST = ED25519-V3). */ + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); + ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, + &pk, &hs_version, NULL); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk.v3); + tt_str_op(key_new_alg, OP_EQ, "ED25519-V3"); + tt_assert(key_new_blob); + tt_ptr_op(reply_str, OP_EQ, NULL); + /* Test discarding the private key. */ + tor_free(reply_str); + tor_free(pk.v3); pk.v3 = NULL; + tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); tt_assert(pk.v3); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); @@ -256,9 +287,10 @@ test_add_onion_helper_keyarg_v3(void *arg) tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); tt_assert(key_blob); + tor_free(reply_str); ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, &key_new_blob, &pk, &hs_version, - &err_msg); + NULL); tor_free(key_blob); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); @@ -266,7 +298,7 @@ test_add_onion_helper_keyarg_v3(void *arg) tt_mem_op(pk.v3, OP_EQ, hex_sk, 64); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); } @@ -274,7 +306,8 @@ test_add_onion_helper_keyarg_v3(void *arg) done: tor_free(pk.v3); tor_free(key_new_blob); - tor_free(err_msg); + tor_free(reply_str); + UNMOCK(control_write_reply); } static void @@ -285,72 +318,61 @@ test_add_onion_helper_keyarg_v2(void *arg) crypto_pk_t *pk1 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; - char *err_msg = NULL; char *encoded = NULL; char *arg_str = NULL; (void) arg; + MOCK(control_write_reply, mock_control_write_reply); memset(&pk, 0, sizeof(pk)); /* Test explicit RSA1024 key generation. */ + tor_free(reply_str); ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); - tt_ptr_op(err_msg, OP_EQ, NULL); - - /* Test "BEST" key generation (Assumes BEST = RSA1024). */ - crypto_pk_free(pk.v2); pk.v2 = NULL; - tor_free(key_new_blob); - ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); - tt_int_op(ret, OP_EQ, 0); - tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); - tt_assert(pk.v2); - tt_str_op(key_new_alg, OP_EQ, "RSA1024"); - tt_assert(key_new_blob); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test discarding the private key. */ crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); - ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + ret = add_onion_helper_keyarg("NEW:RSA1024", 1, &key_new_alg, &key_new_blob, + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); /* Test generating a invalid key type. */ crypto_pk_free(pk.v2); pk.v2 = NULL; ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_assert(err_msg); + tt_assert(reply_str); /* Test loading a RSA1024 key. */ - tor_free(err_msg); + tor_free(reply_str); pk1 = pk_generate(0); tt_int_op(0, OP_EQ, crypto_pk_base64_encode_private(pk1, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, 0); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0); /* Test loading a invalid key type. */ @@ -359,36 +381,37 @@ test_add_onion_helper_keyarg_v2(void *arg) crypto_pk_free(pk.v2); pk.v2 = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_assert(err_msg); + tt_assert(reply_str); /* Test loading a invalid key. */ tor_free(arg_str); crypto_pk_free(pk.v2); pk.v2 = NULL; - tor_free(err_msg); + tor_free(reply_str); encoded[strlen(encoded)/2] = '\0'; tor_asprintf(&arg_str, "RSA1024:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk, &hs_version, &err_msg); + &pk, &hs_version, NULL); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); tt_assert(!pk.v2); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); - tt_assert(err_msg); + tt_assert(reply_str); done: crypto_pk_free(pk1); crypto_pk_free(pk.v2); tor_free(key_new_blob); - tor_free(err_msg); + tor_free(reply_str); tor_free(encoded); tor_free(arg_str); + UNMOCK(control_write_reply); } static void @@ -542,49 +565,52 @@ static void test_add_onion_helper_clientauth(void *arg) { rend_authorized_client_t *client = NULL; - char *err_msg = NULL; int created = 0; (void)arg; + MOCK(control_write_reply, mock_control_write_reply); /* Test "ClientName" only. */ - client = add_onion_helper_clientauth("alice", &created, &err_msg); + tor_free(reply_str); + client = add_onion_helper_clientauth("alice", &created, NULL); tt_assert(client); tt_assert(created); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); rend_authorized_client_free(client); /* Test "ClientName:Blob" */ + tor_free(reply_str); client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B", - &created, &err_msg); + &created, NULL); tt_assert(client); tt_assert(!created); - tt_ptr_op(err_msg, OP_EQ, NULL); + tt_ptr_op(reply_str, OP_EQ, NULL); rend_authorized_client_free(client); /* Test invalid client names */ + tor_free(reply_str); client = add_onion_helper_clientauth("no*asterisks*allowed", &created, - &err_msg); + NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); /* Test invalid auth cookie */ - client = add_onion_helper_clientauth("alice:12345", &created, &err_msg); + tor_free(reply_str); + client = add_onion_helper_clientauth("alice:12345", &created, NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); /* Test invalid syntax */ + tor_free(reply_str); client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created, - &err_msg); + NULL); tt_ptr_op(client, OP_EQ, NULL); - tt_assert(err_msg); - tor_free(err_msg); + tt_assert(reply_str); done: rend_authorized_client_free(client); - tor_free(err_msg); + tor_free(reply_str); + UNMOCK(control_write_reply); } /* Mocks and data/variables used for GETINFO download status tests */ diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c index 910aacace3..9fb2bc7256 100644 --- a/src/test/test_controller_events.c +++ b/src/test/test_controller_events.c @@ -7,6 +7,7 @@ #define CONTROL_EVENTS_PRIVATE #define OCIRC_EVENT_PRIVATE #define ORCONN_EVENT_PRIVATE +#include "app/main/subsysmgr.h" #include "core/or/or.h" #include "core/or/channel.h" #include "core/or/channeltls.h" @@ -16,6 +17,7 @@ #include "core/mainloop/connection.h" #include "feature/control/control_events.h" #include "test/test.h" +#include "test/test_helpers.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" @@ -394,38 +396,40 @@ test_cntev_dirboot_defer_orconn(void *arg) } static void -setup_orconn_state(orconn_event_msg_t *msg, uint64_t gid, uint64_t chan, +setup_orconn_state(orconn_state_msg_t *msg, uint64_t gid, uint64_t chan, int proxy_type) { - msg->type = ORCONN_MSGTYPE_STATE; - msg->u.state.gid = gid; - msg->u.state.chan = chan; - msg->u.state.proxy_type = proxy_type; + msg->gid = gid; + msg->chan = chan; + msg->proxy_type = proxy_type; } static void -send_orconn_state(orconn_event_msg_t *msg, uint8_t state) +send_orconn_state(const orconn_state_msg_t *msg_in, uint8_t state) { - msg->u.state.state = state; - orconn_event_publish(msg); + orconn_state_msg_t *msg = tor_malloc(sizeof(*msg)); + + *msg = *msg_in; + msg->state = state; + orconn_state_publish(msg); } static void send_ocirc_chan(uint32_t gid, uint64_t chan, bool onehop) { - ocirc_event_msg_t msg; + ocirc_chan_msg_t *msg = tor_malloc(sizeof(*msg)); - msg.type = OCIRC_MSGTYPE_CHAN; - msg.u.chan.gid = gid; - msg.u.chan.chan = chan; - msg.u.chan.onehop = onehop; - ocirc_event_publish(&msg); + msg->gid = gid; + msg->chan = chan; + msg->onehop = onehop; + ocirc_chan_publish(msg); } static void test_cntev_orconn_state(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; + memset(&conn, 0, sizeof(conn)); (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -442,8 +446,8 @@ test_cntev_orconn_state(void *arg) send_orconn_state(&conn, OR_CONN_STATE_OPEN); assert_bootmsg("15 TAG=handshake_done"); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); /* It doesn't know it's an origin circuit yet */ assert_bootmsg("15 TAG=handshake_done"); @@ -464,7 +468,8 @@ test_cntev_orconn_state(void *arg) static void test_cntev_orconn_state_pt(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; + memset(&conn, 0, sizeof(conn)); (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -484,8 +489,8 @@ test_cntev_orconn_state_pt(void *arg) assert_bootmsg("15 TAG=handshake_done"); send_ocirc_chan(2, 2, false); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); assert_bootmsg("76 TAG=ap_conn_pt"); send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING); @@ -499,7 +504,8 @@ test_cntev_orconn_state_pt(void *arg) static void test_cntev_orconn_state_proxy(void *arg) { - orconn_event_msg_t conn; + orconn_state_msg_t conn; + memset(&conn, 0, sizeof(conn)); (void)arg; MOCK(queue_control_event_string, mock_queue_control_event_string); @@ -519,8 +525,8 @@ test_cntev_orconn_state_proxy(void *arg) assert_bootmsg("15 TAG=handshake_done"); send_ocirc_chan(2, 2, false); - conn.u.state.gid = 2; - conn.u.state.chan = 2; + conn.gid = 2; + conn.chan = 2; send_orconn_state(&conn, OR_CONN_STATE_CONNECTING); assert_bootmsg("78 TAG=ap_conn_proxy"); send_orconn_state(&conn, OR_CONN_STATE_PROXY_HANDSHAKING); @@ -534,15 +540,18 @@ test_cntev_orconn_state_proxy(void *arg) #define TEST(name, flags) \ { #name, test_cntev_ ## name, flags, 0, NULL } +#define T_PUBSUB(name, setup) \ + { #name, test_cntev_ ## name, TT_FORK, &helper_pubsub_setup, NULL } + struct testcase_t controller_event_tests[] = { TEST(sum_up_cell_stats, TT_FORK), TEST(append_cell_stats, TT_FORK), TEST(format_cell_stats, TT_FORK), TEST(event_mask, TT_FORK), - TEST(dirboot_defer_desc, TT_FORK), - TEST(dirboot_defer_orconn, TT_FORK), - TEST(orconn_state, TT_FORK), - TEST(orconn_state_pt, TT_FORK), - TEST(orconn_state_proxy, TT_FORK), + T_PUBSUB(dirboot_defer_desc, TT_FORK), + T_PUBSUB(dirboot_defer_orconn, TT_FORK), + T_PUBSUB(orconn_state, TT_FORK), + T_PUBSUB(orconn_state_pt, TT_FORK), + T_PUBSUB(orconn_state_proxy, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index d1c652f058..ed39f41560 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1961,7 +1961,7 @@ test_crypto_curve25519_impl(void *arg) "e0544770bc7de853b38f9100489e3e79"; const char e1e2k_expected[] = "cd6e8269104eb5aaee886bd2071fba88" "bd13861475516bc2cd2b6e005e805064"; -#else /* !(defined(SLOW_CURVE25519_TEST)) */ +#else /* !defined(SLOW_CURVE25519_TEST) */ const int loop_max=200; const char e1_expected[] = "bc7112cde03f97ef7008cad1bdc56be3" "c6a1037d74cceb3712e9206871dcf654"; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 17d6db1e4d..6329ff7750 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -26,7 +26,7 @@ #include "core/or/or.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" #include "core/mainloop/connection.h" #include "core/or/relay.h" #include "core/or/versions.h" diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index e57bd02584..edfd0c74e1 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -479,8 +479,7 @@ static or_options_t *mock_options = NULL; static void init_mock_options(void) { - mock_options = tor_malloc(sizeof(or_options_t)); - memset(mock_options, 0, sizeof(or_options_t)); + mock_options = options_new(); mock_options->TestingTorNetwork = 1; mock_options->DataDirectory = tor_strdup(get_fname_rnd("datadir_tmp")); mock_options->CacheDirectory = tor_strdup(mock_options->DataDirectory); diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index fc7c5d5800..8f2d507743 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -11,7 +11,7 @@ #include "feature/client/addressmap.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" #include "core/mainloop/connection.h" #include "core/or/connection_edge.h" #include "feature/nodelist/nodelist.h" diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index e0897dd2ca..d59b1c7153 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -5,6 +5,7 @@ #define CIRCUITLIST_PRIVATE #define CIRCUITBUILD_PRIVATE +#define CONFIG_PRIVATE #define STATEFILE_PRIVATE #define ENTRYNODES_PRIVATE #define ROUTERLIST_PRIVATE @@ -17,7 +18,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" #include "lib/crypt_ops/crypto_rand.h" #include "feature/dircommon/directory.h" #include "feature/dirclient/dirclient.h" @@ -201,7 +202,7 @@ big_fake_network_setup(const struct testcase_t *testcase) smartlist_add(big_fake_net_nodes, n); } - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t)); if (reasonably_future_consensus) { /* Make the dummy consensus valid in 6 hours, and expiring in 7 hours. */ @@ -235,12 +236,12 @@ mock_randomize_time_no_randomization(time_t a, time_t b) return a; } -static or_options_t mocked_options; +static or_options_t *mocked_options; static const or_options_t * mock_get_options(void) { - return &mocked_options; + return mocked_options; } #define TEST_IPV4_ADDR "123.45.67.89" @@ -259,7 +260,7 @@ test_node_preferred_orport(void *arg) tor_addr_port_t ap; /* Setup options */ - memset(&mocked_options, 0, sizeof(mocked_options)); + mocked_options = options_new(); /* We don't test ClientPreferIPv6ORPort here, because it's used in * nodelist_set_consensus to setup node.ipv6_preferred, which we set * directly. */ @@ -282,8 +283,8 @@ test_node_preferred_orport(void *arg) /* Check the preferred address is IPv4 if we're only using IPv4, regardless * of whether we prefer it or not */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 0; + mocked_options->ClientUseIPv4 = 1; + mocked_options->ClientUseIPv6 = 0; node.ipv6_preferred = 0; node_get_pref_orport(&node, &ap); tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); @@ -296,8 +297,8 @@ test_node_preferred_orport(void *arg) /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but * don't prefer the IPv6 address */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; + mocked_options->ClientUseIPv4 = 1; + mocked_options->ClientUseIPv6 = 1; node.ipv6_preferred = 0; node_get_pref_orport(&node, &ap); tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); @@ -305,28 +306,29 @@ test_node_preferred_orport(void *arg) /* Check the preferred address is IPv6 if we prefer it and * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; + mocked_options->ClientUseIPv4 = 1; + mocked_options->ClientUseIPv6 = 1; node.ipv6_preferred = 1; node_get_pref_orport(&node, &ap); tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); tt_assert(ap.port == ipv6_port); - mocked_options.ClientUseIPv4 = 0; + mocked_options->ClientUseIPv4 = 0; node_get_pref_orport(&node, &ap); tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); tt_assert(ap.port == ipv6_port); /* Check the preferred address is IPv6 if we don't prefer it, but * ClientUseIPv4 is 0 */ - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options); + mocked_options->ClientUseIPv4 = 0; + mocked_options->ClientUseIPv6 = 1; + node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(mocked_options); node_get_pref_orport(&node, &ap); tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); tt_assert(ap.port == ipv6_port); done: + or_options_free(mocked_options); UNMOCK(get_options); } diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index 38aca90266..cb53a4e662 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -587,6 +587,6 @@ struct testcase_t extorport_tests[] = { { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL }, { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK, NULL, NULL }, - { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL }, + { "handshake", test_ext_or_handshake, TT_FORK, &helper_pubsub_setup, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 489c257761..8eb3c2c928 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -16,13 +16,18 @@ #include "lib/buf/buffers.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" +#include "app/main/subsysmgr.h" #include "core/mainloop/connection.h" #include "lib/crypt_ops/crypto_rand.h" #include "core/mainloop/mainloop.h" #include "feature/nodelist/nodelist.h" #include "core/or/relay.h" #include "feature/nodelist/routerlist.h" +#include "lib/dispatch/dispatch.h" +#include "lib/dispatch/dispatch_naming.h" +#include "lib/pubsub/pubsub_build.h" +#include "lib/pubsub/pubsub_connect.h" #include "lib/encoding/confline.h" #include "lib/net/resolve.h" @@ -290,7 +295,7 @@ helper_parse_options(const char *conf) if (ret != 0) { goto done; } - ret = config_assign(&options_format, opt, line, 0, &msg); + ret = config_assign(get_options_mgr(), opt, line, 0, &msg); if (ret != 0) { goto done; } @@ -303,3 +308,54 @@ helper_parse_options(const char *conf) } return opt; } + +/** + * Dispatch alertfn callback: flush all messages right now. Implements + * DELIV_IMMEDIATE. + **/ +static void +alertfn_immediate(dispatch_t *d, channel_id_t chan, void *arg) +{ + (void) arg; + dispatch_flush(d, chan, INT_MAX); +} + +/** + * Setup helper for tests that need pubsub active + * + * Does not hook up mainloop events. Does set immediate delivery for + * all channels. + */ +void * +helper_setup_pubsub(const struct testcase_t *testcase) +{ + dispatch_t *dispatcher = NULL; + pubsub_builder_t *builder = pubsub_builder_new(); + channel_id_t chan = get_channel_id("orconn"); + + (void)testcase; + (void)subsystems_add_pubsub(builder); + dispatcher = pubsub_builder_finalize(builder, NULL); + tor_assert(dispatcher); + dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL); + chan = get_channel_id("ocirc"); + dispatch_set_alert_fn(dispatcher, chan, alertfn_immediate, NULL); + return dispatcher; +} + +/** + * Cleanup helper for tests that need pubsub active + */ +int +helper_cleanup_pubsub(const struct testcase_t *testcase, void *dispatcher_) +{ + dispatch_t *dispatcher = dispatcher_; + + (void)testcase; + dispatch_free(dispatcher); + return 1; +} + +const struct testcase_setup_t helper_pubsub_setup = { + helper_setup_pubsub, helper_cleanup_pubsub +}; diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 9e376a563d..d82072bb34 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -7,6 +7,7 @@ #define BUFFERS_PRIVATE #include "core/or/or.h" +#include "tinytest.h" const char *get_yesterday_date_str(void); @@ -31,5 +32,10 @@ or_options_t *helper_parse_options(const char *conf); extern const char TEST_DESCRIPTORS[]; +void *helper_setup_pubsub(const struct testcase_t *); +int helper_cleanup_pubsub(const struct testcase_t *, void *); + +extern const struct testcase_setup_t helper_pubsub_setup; + #endif /* !defined(TOR_TEST_HELPERS_H) */ diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c index d71f8b6b18..86ac7e7fb1 100644 --- a/src/test/test_hs_cache.c +++ b/src/test/test_hs_cache.c @@ -10,6 +10,7 @@ #define DIRCACHE_PRIVATE #define DIRCLIENT_PRIVATE #define HS_CACHE_PRIVATE +#define TOR_CHANNEL_INTERNAL_ #include "trunnel/ed25519_cert.h" #include "feature/hs/hs_cache.h" @@ -20,7 +21,12 @@ #include "core/mainloop/connection.h" #include "core/proto/proto_http.h" #include "lib/crypt_ops/crypto_format.h" +#include "core/or/circuitlist.h" +#include "core/or/channel.h" +#include "core/or/edge_connection_st.h" +#include "core/or/or_circuit_st.h" +#include "core/or/or_connection_st.h" #include "feature/dircommon/dir_connection_st.h" #include "feature/nodelist/networkstatus_st.h" @@ -232,6 +238,8 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) /* The dir conn we are going to simulate */ dir_connection_t *conn = NULL; + edge_connection_t *edge_conn = NULL; + or_circuit_t *or_circ = NULL; /* First extract the blinded public key that we are going to use in our query, and then build the actual query string. */ @@ -245,8 +253,16 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) /* Simulate an HTTP GET request to the HSDir */ conn = dir_connection_new(AF_INET); tt_assert(conn); + TO_CONN(conn)->linked = 1; /* Signal that it is encrypted. */ tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); - TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */ + + /* Pretend this conn is anonymous. */ + edge_conn = edge_connection_new(CONN_TYPE_EXIT, AF_INET); + TO_CONN(conn)->linked_conn = TO_CONN(edge_conn); + or_circ = or_circuit_new(0, NULL); + or_circ->p_chan = tor_malloc_zero(sizeof(channel_t)); + edge_conn->on_circuit = TO_CIRCUIT(or_circ); + retval = directory_handle_command_get(conn, hsdir_query_str, NULL, 0); tt_int_op(retval, OP_EQ, 0); @@ -263,8 +279,11 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) done: tor_free(hsdir_query_str); - if (conn) + if (conn) { + tor_free(or_circ->p_chan); + connection_free_minimal(TO_CONN(conn)->linked_conn); connection_free_minimal(TO_CONN(conn)); + } return received_desc; } diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c index cdcbe23e69..403509fbc8 100644 --- a/src/test/test_hs_cell.c +++ b/src/test/test_hs_cell.c @@ -20,6 +20,7 @@ #include "feature/hs/hs_service.h" /* Trunnel. */ +#include "trunnel/hs/cell_common.h" #include "trunnel/hs/cell_establish_intro.h" /** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we @@ -38,11 +39,13 @@ test_gen_establish_intro_cell(void *arg) /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we attempt to parse it. */ { + hs_service_config_t config; + memset(&config, 0, sizeof(config)); /* We only need the auth key pair here. */ hs_service_intro_point_t *ip = service_intro_point_new(NULL); /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ - ret = hs_cell_build_establish_intro(circ_nonce, ip, buf); + ret = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf); service_intro_point_free(ip); tt_u64_op(ret, OP_GT, 0); } @@ -97,6 +100,9 @@ test_gen_establish_intro_cell_bad(void *arg) trn_cell_establish_intro_t *cell = NULL; char circ_nonce[DIGEST_LEN] = {0}; hs_service_intro_point_t *ip = NULL; + hs_service_config_t config; + + memset(&config, 0, sizeof(config)); MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed); @@ -108,7 +114,7 @@ test_gen_establish_intro_cell_bad(void *arg) cell = trn_cell_establish_intro_new(); tt_assert(cell); ip = service_intro_point_new(NULL); - cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL); + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, NULL); service_intro_point_free(ip); expect_log_msg_containing("Unable to make signature for " "ESTABLISH_INTRO cell."); @@ -120,11 +126,97 @@ test_gen_establish_intro_cell_bad(void *arg) UNMOCK(ed25519_sign_prefixed); } +static void +test_gen_establish_intro_dos_ext(void *arg) +{ + ssize_t ret; + hs_service_config_t config; + hs_service_intro_point_t *ip = NULL; + trn_cell_extension_t *extensions = NULL; + trn_cell_extension_dos_t *dos = NULL; + + (void) arg; + + memset(&config, 0, sizeof(config)); + ip = service_intro_point_new(NULL); + tt_assert(ip); + ip->support_intro2_dos_defense = 1; + + /* Case 1: No DoS parameters so no extension to be built. */ + extensions = build_establish_intro_extensions(&config, ip); + tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 0); + trn_cell_extension_free(extensions); + extensions = NULL; + + /* Case 2: Enable the DoS extension. Parameter set to 0 should indicate to + * disable the defense on the intro point but there should be an extension + * nonetheless in the cell. */ + config.has_dos_defense_enabled = 1; + extensions = build_establish_intro_extensions(&config, ip); + tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1); + /* Validate the extension. */ + const trn_cell_extension_field_t *field = + trn_cell_extension_getconst_fields(extensions, 0); + tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ, + TRUNNEL_CELL_EXTENSION_TYPE_DOS); + ret = trn_cell_extension_dos_parse(&dos, + trn_cell_extension_field_getconstarray_field(field), + trn_cell_extension_field_getlen_field(field)); + tt_int_op(ret, OP_EQ, 19); + /* Rate per sec param. */ + const trn_cell_extension_dos_param_t *param = + trn_cell_extension_dos_getconst_params(dos, 0); + tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ, + TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC); + tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0); + /* Burst per sec param. */ + param = trn_cell_extension_dos_getconst_params(dos, 1); + tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ, + TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC); + tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0); + trn_cell_extension_dos_free(dos); dos = NULL; + trn_cell_extension_free(extensions); extensions = NULL; + + /* Case 3: Enable the DoS extension. Parameter set to some normal values. */ + config.has_dos_defense_enabled = 1; + config.intro_dos_rate_per_sec = 42; + config.intro_dos_burst_per_sec = 250; + extensions = build_establish_intro_extensions(&config, ip); + tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1); + /* Validate the extension. */ + field = trn_cell_extension_getconst_fields(extensions, 0); + tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ, + TRUNNEL_CELL_EXTENSION_TYPE_DOS); + ret = trn_cell_extension_dos_parse(&dos, + trn_cell_extension_field_getconstarray_field(field), + trn_cell_extension_field_getlen_field(field)); + tt_int_op(ret, OP_EQ, 19); + /* Rate per sec param. */ + param = trn_cell_extension_dos_getconst_params(dos, 0); + tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ, + TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC); + tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 42); + /* Burst per sec param. */ + param = trn_cell_extension_dos_getconst_params(dos, 1); + tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ, + TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC); + tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 250); + trn_cell_extension_dos_free(dos); dos = NULL; + trn_cell_extension_free(extensions); extensions = NULL; + + done: + service_intro_point_free(ip); + trn_cell_extension_dos_free(dos); + trn_cell_extension_free(extensions); +} + struct testcase_t hs_cell_tests[] = { { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, NULL, NULL }, { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK, NULL, NULL }, + { "gen_establish_intro_dos_ext", test_gen_establish_intro_dos_ext, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 0d25a98bb3..b777dafdfb 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -37,6 +37,7 @@ #include "feature/hs/hs_config.h" #include "feature/hs/hs_ident.h" #include "feature/hs/hs_cache.h" +#include "feature/rend/rendcache.h" #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" #include "core/mainloop/connection.h" @@ -159,8 +160,7 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out, or_circ->rend_data = rend_data_dup(conn_rend_data); } else { /* prop224: Setup hs ident on the circuit */ - or_circ->hs_ident = hs_ident_circuit_new(&service_pk, - HS_IDENT_CIRCUIT_RENDEZVOUS); + or_circ->hs_ident = hs_ident_circuit_new(&service_pk); } TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; @@ -963,8 +963,7 @@ test_close_intro_circuits_new_desc(void *arg) const hs_desc_intro_point_t *ip = smartlist_get(desc1->encrypted_data.intro_points, 0); tt_assert(ip); - ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey, - HS_IDENT_CIRCUIT_INTRO); + ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey); ed25519_pubkey_copy(ô->hs_ident->intro_auth_pk, &ip->auth_key_cert->signed_key); } @@ -1007,6 +1006,91 @@ test_close_intro_circuits_new_desc(void *arg) UNMOCK(networkstatus_get_live_consensus); } +static void +test_close_intro_circuits_cache_clean(void *arg) +{ + int ret; + ed25519_keypair_t service_kp; + circuit_t *circ = NULL; + origin_circuit_t *ocirc = NULL; + hs_descriptor_t *desc1 = NULL; + + (void) arg; + + hs_init(); + rend_cache_init(); + + /* This is needed because of the client cache expiration timestamp is based + * on having a consensus. See cached_client_descriptor_has_expired(). */ + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + /* Set consensus time */ + parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC", + &mock_ns.valid_until); + + /* Generate service keypair */ + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0)); + + /* Create and add to the global list a dummy client introduction circuits. + * We'll then make sure the hs_ident is attached to a dummy descriptor. */ + circ = dummy_origin_circuit_new(0); + tt_assert(circ); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING; + ocirc = TO_ORIGIN_CIRCUIT(circ); + + /* Build the first descriptor and cache it. */ + { + char *encoded; + desc1 = hs_helper_build_hs_desc_with_ip(&service_kp); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded); + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + /* Store it */ + ret = hs_cache_store_as_client(encoded, &service_kp.pubkey); + tt_int_op(ret, OP_EQ, 0); + tor_free(encoded); + tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey)); + } + + /* We'll pick one introduction point and associate it with the circuit. */ + { + const hs_desc_intro_point_t *ip = + smartlist_get(desc1->encrypted_data.intro_points, 0); + tt_assert(ip); + ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey); + ed25519_pubkey_copy(ô->hs_ident->intro_auth_pk, + &ip->auth_key_cert->signed_key); + } + + /* Before we are about to clean up the intro circuits, make sure it is + * actually there. */ + tt_assert(circuit_get_next_intro_circ(NULL, true)); + + /* Cleanup the client cache. The ns valid after time is what decides if the + * descriptor has expired so put it in the future enough (72h) so we are + * sure to always expire. */ + mock_ns.valid_after = approx_time() + (72 * 24 * 60 * 60); + hs_cache_clean_as_client(0); + + /* Once stored, our intro circuit should be closed because it is related to + * an old introduction point that doesn't exists anymore. */ + tt_assert(!circuit_get_next_intro_circ(NULL, true)); + + done: + circuit_free(circ); + hs_descriptor_free(desc1); + hs_free_all(); + rend_cache_free_all(); + UNMOCK(networkstatus_get_live_consensus); +} + struct testcase_t hs_client_tests[] = { { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy, TT_FORK, NULL, NULL }, @@ -1026,6 +1110,8 @@ struct testcase_t hs_client_tests[] = { TT_FORK, NULL, NULL }, { "close_intro_circuits_new_desc", test_close_intro_circuits_new_desc, TT_FORK, NULL, NULL }, + { "close_intro_circuits_cache_clean", test_close_intro_circuits_cache_clean, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index abded6021e..de3f7e04f7 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -502,6 +502,7 @@ test_desc_reupload_logic(void *arg) pubkey_hex, strlen(pubkey_hex)); hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr); service = tor_malloc_zero(sizeof(hs_service_t)); + tt_assert(service); memcpy(service->onion_address, onion_addr, sizeof(service->onion_address)); ed25519_secret_key_generate(&service->keys.identity_sk, 0); ed25519_public_key_generate(&service->keys.identity_pk, diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c index c2c556307d..71e1529216 100644 --- a/src/test/test_hs_config.c +++ b/src/test/test_hs_config.c @@ -12,6 +12,7 @@ #include "test/test.h" #include "test/test_helpers.h" #include "test/log_test_helpers.h" +#include "test/resolve_test_helpers.h" #include "app/config/config.h" #include "feature/hs/hs_common.h" @@ -272,6 +273,7 @@ test_valid_service_v2(void *arg) int ret; (void) arg; + mock_hostname_resolver(); /* Valid complex configuration. Basic client authorization. */ { @@ -314,7 +316,7 @@ test_valid_service_v2(void *arg) } done: - ; + unmock_hostname_resolver(); } static void @@ -392,6 +394,7 @@ test_valid_service_v3(void *arg) int ret; (void) arg; + mock_hostname_resolver(); /* Valid complex configuration. */ { @@ -448,7 +451,7 @@ test_valid_service_v3(void *arg) } done: - ; + unmock_hostname_resolver(); } static void @@ -489,6 +492,111 @@ test_staging_service_v3(void *arg) hs_free_all(); } +static void +test_dos_parameters(void *arg) +{ + int ret; + + (void) arg; + + hs_init(); + + /* Valid configuration. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServiceEnableIntroDoSDefense 1\n" + "HiddenServiceEnableIntroDoSRatePerSec 42\n" + "HiddenServiceEnableIntroDoSBurstPerSec 87\n"; + + setup_full_capture_of_logs(LOG_INFO); + ret = helper_config_service(conf, 0); + tt_int_op(ret, OP_EQ, 0); + expect_log_msg_containing("Service INTRO2 DoS defenses rate set to: 42"); + expect_log_msg_containing("Service INTRO2 DoS defenses burst set to: 87"); + teardown_capture_of_logs(); + } + + /* Invalid rate. Value of 2^37. Max allowed is 2^31. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServiceEnableIntroDoSDefense 1\n" + "HiddenServiceEnableIntroDoSRatePerSec 137438953472\n" + "HiddenServiceEnableIntroDoSBurstPerSec 87\n"; + + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 0); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must " + "be between 0 and 2147483647, " + "not 137438953472"); + teardown_capture_of_logs(); + } + + /* Invalid burst. Value of 2^38. Max allowed is 2^31. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServiceEnableIntroDoSDefense 1\n" + "HiddenServiceEnableIntroDoSRatePerSec 42\n" + "HiddenServiceEnableIntroDoSBurstPerSec 274877906944\n"; + + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 0); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceEnableIntroDoSBurstPerSec must " + "be between 0 and 2147483647, " + "not 274877906944"); + teardown_capture_of_logs(); + } + + /* Burst is smaller than rate. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServiceEnableIntroDoSDefense 1\n" + "HiddenServiceEnableIntroDoSRatePerSec 42\n" + "HiddenServiceEnableIntroDoSBurstPerSec 27\n"; + + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 0); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("Hidden service DoS defenses burst (27) can " + "not be smaller than the rate value (42)."); + teardown_capture_of_logs(); + } + + /* Negative value. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServiceEnableIntroDoSDefense 1\n" + "HiddenServiceEnableIntroDoSRatePerSec -1\n" + "HiddenServiceEnableIntroDoSBurstPerSec 42\n"; + + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 0); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceEnableIntroDoSRatePerSec must be " + "between 0 and 2147483647, not -1"); + teardown_capture_of_logs(); + } + + done: + hs_free_all(); +} + struct testcase_t hs_config_tests[] = { /* Invalid service not specific to any version. */ { "invalid_service", test_invalid_service, TT_FORK, @@ -512,6 +620,9 @@ struct testcase_t hs_config_tests[] = { { "staging_service_v3", test_staging_service_v3, TT_FORK, NULL, NULL }, + /* Test HS DoS parameters. */ + { "dos_parameters", test_dos_parameters, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES }; - diff --git a/src/test/test_hs_dos.c b/src/test/test_hs_dos.c new file mode 100644 index 0000000000..f68639e24a --- /dev/null +++ b/src/test/test_hs_dos.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2017-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_cell.c + * \brief Test hidden service cell functionality. + */ + +#define CIRCUITLIST_PRIVATE +#define NETWORKSTATUS_PRIVATE +#define HS_DOS_PRIVATE +#define HS_INTROPOINT_PRIVATE + +#include "test/test.h" +#include "test/test_helpers.h" +#include "test/log_test_helpers.h" + +#include "app/config/config.h" + +#include "core/or/circuitlist.h" +#include "core/or/circuituse.h" +#include "core/or/or_circuit_st.h" + +#include "feature/hs/hs_dos.h" +#include "feature/hs/hs_intropoint.h" +#include "feature/nodelist/networkstatus.h" + +static void +setup_mock_consensus(void) +{ + current_ns_consensus = tor_malloc_zero(sizeof(networkstatus_t)); + current_ns_consensus->net_params = smartlist_new(); + smartlist_add(current_ns_consensus->net_params, + (void *) "HiddenServiceEnableIntroDoSDefense=1"); + hs_dos_consensus_has_changed(current_ns_consensus); +} + +static void +free_mock_consensus(void) +{ + smartlist_free(current_ns_consensus->net_params); + tor_free(current_ns_consensus); +} + +static void +test_can_send_intro2(void *arg) +{ + uint32_t now = (uint32_t) approx_time(); + or_circuit_t *or_circ = NULL; + + (void) arg; + + hs_init(); + hs_dos_init(); + + get_options_mutable()->ORPort_set = 1; + setup_mock_consensus(); + + or_circ = or_circuit_new(1, NULL); + + /* Make that circuit a service intro point. */ + circuit_change_purpose(TO_CIRCUIT(or_circ), CIRCUIT_PURPOSE_INTRO_POINT); + hs_dos_setup_default_intro2_defenses(or_circ); + or_circ->introduce2_dos_defense_enabled = 1; + + /* Brand new circuit, we should be able to send INTRODUCE2 cells. */ + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + + /* Simulate that 10 cells have arrived in 1 second. There should be no + * refill since the bucket is already at maximum on the first cell. */ + update_approx_time(++now); + for (int i = 0; i < 10; i++) { + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + } + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + get_intro2_burst_consensus_param(NULL) - 10); + + /* Fully refill the bucket minus 1 cell. */ + update_approx_time(++now); + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + get_intro2_burst_consensus_param(NULL) - 1); + + /* Receive an INTRODUCE2 at each second. We should have the bucket full + * since at every second it gets refilled. */ + for (int i = 0; i < 10; i++) { + update_approx_time(++now); + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + } + /* Last check if we can send the cell decrements the bucket so minus 1. */ + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + get_intro2_burst_consensus_param(NULL) - 1); + + /* Manually reset bucket for next test. */ + token_bucket_ctr_reset(&or_circ->introduce2_bucket, now); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + get_intro2_burst_consensus_param(NULL)); + + /* Do a full burst in the current second which should empty the bucket and + * we shouldn't be allowed to send one more cell after that. We go minus 1 + * cell else the very last check if we can send the INTRO2 cell returns + * false because the bucket goes down to 0. */ + for (uint32_t i = 0; i < get_intro2_burst_consensus_param(NULL) - 1; i++) { + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + } + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 1); + /* Get the last remaining cell, we shouldn't be allowed to send it. */ + tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0); + + /* Make sure the next 100 cells aren't allowed and bucket stays at 0. */ + for (int i = 0; i < 100; i++) { + tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0); + } + + /* One second has passed, we should have the rate minus 1 cell added. */ + update_approx_time(++now); + tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); + tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, + get_intro2_rate_consensus_param(NULL) - 1); + + done: + circuit_free_(TO_CIRCUIT(or_circ)); + + hs_free_all(); + free_mock_consensus(); +} + +static void +test_validate_dos_extension_params(void *arg) +{ + bool ret; + + (void) arg; + + /* Validate the default values. */ + ret = cell_dos_extension_parameters_are_valid( + get_intro2_rate_consensus_param(NULL), + get_intro2_burst_consensus_param(NULL)); + tt_assert(ret); + + /* Valid custom rate/burst. */ + ret = cell_dos_extension_parameters_are_valid(17, 42); + tt_assert(ret); + ret = cell_dos_extension_parameters_are_valid(INT32_MAX, INT32_MAX); + tt_assert(ret); + + /* Invalid rate. */ + ret = cell_dos_extension_parameters_are_valid(UINT64_MAX, 42); + tt_assert(!ret); + + /* Invalid burst. */ + ret = cell_dos_extension_parameters_are_valid(42, UINT64_MAX); + tt_assert(!ret); + + /* Value of 0 is valid (but should disable defenses) */ + ret = cell_dos_extension_parameters_are_valid(0, 0); + tt_assert(ret); + + /* Can't have burst smaller than rate. */ + ret = cell_dos_extension_parameters_are_valid(42, 40); + tt_assert(!ret); + + done: + return; +} + +struct testcase_t hs_dos_tests[] = { + { "can_send_intro2", test_can_send_intro2, TT_FORK, + NULL, NULL }, + { "validate_dos_extension_params", test_validate_dos_extension_params, + TT_FORK, NULL, NULL }, + + END_OF_TESTCASES +}; diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 732836fb5b..feb934d93c 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -16,6 +16,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "core/or/or.h" +#include "core/or/channel.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "ht.h" @@ -25,6 +26,8 @@ #include "feature/hs/hs_cell.h" #include "feature/hs/hs_circuitmap.h" #include "feature/hs/hs_common.h" +#include "feature/hs/hs_config.h" +#include "feature/hs/hs_dos.h" #include "feature/hs/hs_intropoint.h" #include "feature/hs/hs_service.h" @@ -43,6 +46,9 @@ new_establish_intro_cell(const char *circ_nonce, uint8_t buf[RELAY_PAYLOAD_SIZE] = {0}; trn_cell_establish_intro_t *cell = NULL; hs_service_intro_point_t *ip = NULL; + hs_service_config_t config; + + memset(&config, 0, sizeof(config)); /* Ensure that *cell_out is NULL such that we can use to check if we need to * free `cell` in case of an error. */ @@ -52,7 +58,7 @@ new_establish_intro_cell(const char *circ_nonce, * using this IP object. */ ip = service_intro_point_new(NULL); tt_assert(ip); - cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf); + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf); tt_i64_op(cell_len, OP_GT, 0); cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf)); @@ -73,12 +79,15 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out) { ssize_t cell_len = 0; hs_service_intro_point_t *ip = NULL; + hs_service_config_t config; + + memset(&config, 0, sizeof(config)); /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ ip = service_intro_point_new(NULL); tt_assert(ip); - cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out); + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell_out); tt_i64_op(cell_len, OP_GT, 0); done: @@ -118,6 +127,8 @@ helper_create_intro_circuit(void) or_circuit_t *circ = or_circuit_new(0, NULL); tt_assert(circ); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100, + (uint32_t) approx_time()); done: return circ; } @@ -693,6 +704,17 @@ test_introduce1_suitable_circuit(void *arg) tt_int_op(ret, OP_EQ, 0); } + /* Single hop circuit should not be allowed. */ + { + circ = or_circuit_new(0, NULL); + circ->p_chan = tor_malloc_zero(sizeof(channel_t)); + circ->p_chan->is_client = 1; + ret = circuit_is_suitable_for_introduce1(circ); + tor_free(circ->p_chan); + circuit_free_(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + done: ; } @@ -888,43 +910,213 @@ test_received_introduce1_handling(void *arg) UNMOCK(relay_send_command_from_edge_); } +static void +test_received_establish_intro_dos_ext(void *arg) +{ + int ret; + ssize_t cell_len = 0; + uint8_t cell[RELAY_PAYLOAD_SIZE] = {0}; + char circ_nonce[DIGEST_LEN] = {0}; + hs_service_intro_point_t *ip = NULL; + hs_service_config_t config; + or_circuit_t *intro_circ = or_circuit_new(0,NULL); + + (void) arg; + + MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); + + hs_circuitmap_init(); + + /* Setup. */ + crypto_rand(circ_nonce, sizeof(circ_nonce)); + ip = service_intro_point_new(NULL); + tt_assert(ip); + ip->support_intro2_dos_defense = 1; + memset(&config, 0, sizeof(config)); + config.has_dos_defense_enabled = 1; + config.intro_dos_rate_per_sec = 13; + config.intro_dos_burst_per_sec = 42; + helper_prepare_circ_for_intro(intro_circ, circ_nonce); + /* The INTRO2 bucket should be 0 at this point. */ + tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, 0); + tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, 0); + tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, 0); + tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, 0); + + /* Case 1: Build encoded cell. Usable DoS parameters. */ + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell); + tt_size_op(cell_len, OP_GT, 0); + /* Pass it to the intro point. */ + ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len); + tt_int_op(ret, OP_EQ, 0); + /* Should be set to the burst value. */ + tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, 42); + /* Validate the config of the intro2 bucket. */ + tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, 13); + tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, 42); + tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, 1); + + /* Need to reset the circuit in between test cases. */ + circuit_free_(TO_CIRCUIT(intro_circ)); + intro_circ = or_circuit_new(0,NULL); + helper_prepare_circ_for_intro(intro_circ, circ_nonce); + + /* Case 2: Build encoded cell. Bad DoS parameters. */ + config.has_dos_defense_enabled = 1; + config.intro_dos_rate_per_sec = UINT_MAX; + config.intro_dos_burst_per_sec = 13; + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell); + tt_size_op(cell_len, OP_GT, 0); + /* Pass it to the intro point. */ + ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len); + tt_int_op(ret, OP_EQ, 0); + tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_DEFAULT); + + /* Need to reset the circuit in between test cases. */ + circuit_free_(TO_CIRCUIT(intro_circ)); + intro_circ = or_circuit_new(0,NULL); + helper_prepare_circ_for_intro(intro_circ, circ_nonce); + + /* Case 3: Build encoded cell. Burst is smaller than rate. Not allowed. */ + config.has_dos_defense_enabled = 1; + config.intro_dos_rate_per_sec = 87; + config.intro_dos_burst_per_sec = 45; + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell); + tt_size_op(cell_len, OP_GT, 0); + /* Pass it to the intro point. */ + ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len); + tt_int_op(ret, OP_EQ, 0); + tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_DEFAULT); + + /* Need to reset the circuit in between test cases. */ + circuit_free_(TO_CIRCUIT(intro_circ)); + intro_circ = or_circuit_new(0,NULL); + helper_prepare_circ_for_intro(intro_circ, circ_nonce); + + /* Case 4: Build encoded cell. Rate is 0 but burst is not 0. Disables the + * defense. */ + config.has_dos_defense_enabled = 1; + config.intro_dos_rate_per_sec = 0; + config.intro_dos_burst_per_sec = 45; + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell); + tt_size_op(cell_len, OP_GT, 0); + /* Pass it to the intro point. */ + ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len); + tt_int_op(ret, OP_EQ, 0); + tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_DEFAULT); + + /* Need to reset the circuit in between test cases. */ + circuit_free_(TO_CIRCUIT(intro_circ)); + intro_circ = or_circuit_new(0,NULL); + helper_prepare_circ_for_intro(intro_circ, circ_nonce); + + /* Case 5: Build encoded cell. Burst is 0 but rate is not 0. Disables the + * defense. */ + config.has_dos_defense_enabled = 1; + config.intro_dos_rate_per_sec = 45; + config.intro_dos_burst_per_sec = 0; + cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell); + tt_size_op(cell_len, OP_GT, 0); + /* Pass it to the intro point. */ + ret = hs_intro_received_establish_intro(intro_circ, cell, cell_len); + tt_int_op(ret, OP_EQ, 0); + tt_u64_op(token_bucket_ctr_get(&intro_circ->introduce2_bucket), OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_u64_op(intro_circ->introduce2_bucket.cfg.rate, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_bucket.cfg.burst, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_DEFAULT); + tt_int_op(intro_circ->introduce2_dos_defense_enabled, OP_EQ, + HS_CONFIG_V3_DOS_DEFENSE_DEFAULT); + + done: + circuit_free_(TO_CIRCUIT(intro_circ)); + service_intro_point_free(ip); + hs_circuitmap_free_all(); + UNMOCK(relay_send_command_from_edge_); +} + +static void * +hs_subsystem_setup_fn(const struct testcase_t *tc) +{ + (void) tc; + + return NULL; +} + +static int +hs_subsystem_cleanup_fn(const struct testcase_t *tc, void *arg) +{ + (void) tc; + (void) arg; + + return 1; +} + +static struct testcase_setup_t test_setup = { + hs_subsystem_setup_fn, hs_subsystem_cleanup_fn +}; + struct testcase_t hs_intropoint_tests[] = { { "intro_point_registration", - test_intro_point_registration, TT_FORK, NULL, NULL }, + test_intro_point_registration, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_keytype", - test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_keytype, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_keytype2", - test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_keytype2, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_purpose", - test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_purpose, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_sig", - test_establish_intro_wrong_sig, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_sig, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_sig_len", - test_establish_intro_wrong_sig_len, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_sig_len, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_auth_key_len", - test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, &test_setup}, { "receive_establish_intro_wrong_mac", - test_establish_intro_wrong_mac, TT_FORK, NULL, NULL }, + test_establish_intro_wrong_mac, TT_FORK, NULL, &test_setup}, { "introduce1_suitable_circuit", - test_introduce1_suitable_circuit, TT_FORK, NULL, NULL }, + test_introduce1_suitable_circuit, TT_FORK, NULL, &test_setup}, { "introduce1_is_legacy", - test_introduce1_is_legacy, TT_FORK, NULL, NULL }, + test_introduce1_is_legacy, TT_FORK, NULL, &test_setup}, { "introduce1_validation", - test_introduce1_validation, TT_FORK, NULL, NULL }, + test_introduce1_validation, TT_FORK, NULL, &test_setup}, { "received_introduce1_handling", - test_received_introduce1_handling, TT_FORK, NULL, NULL }, + test_received_introduce1_handling, TT_FORK, NULL, &test_setup}, + + { "received_establish_intro_dos_ext", + test_received_establish_intro_dos_ext, TT_FORK, NULL, &test_setup}, END_OF_TESTCASES }; - diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index a303f10411..66194cee3d 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -171,8 +171,7 @@ test_e2e_rend_circuit_setup(void *arg) tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0)); tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk)); - or_circ->hs_ident = hs_ident_circuit_new(&service_pk, - HS_IDENT_CIRCUIT_RENDEZVOUS); + or_circ->hs_ident = hs_ident_circuit_new(&service_pk); TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; } @@ -675,9 +674,11 @@ test_service_intro_point(void *arg) (void) arg; + update_approx_time(1481621834); + /* Test simple creation of an object. */ { - time_t now = time(NULL); + time_t now = approx_time(); ip = helper_create_service_ip(); tt_assert(ip); /* Make sure the authentication keypair is not zeroes. */ @@ -1105,8 +1106,7 @@ test_closing_intro_circs(void *arg) /* Initialize intro circuit */ intro_circ = origin_circuit_init(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, flags); - intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk, - HS_IDENT_CIRCUIT_INTRO); + intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk); /* Register circuit in the circuitmap . */ hs_circuitmap_register_intro_circ_v3_service_side(intro_circ, &ip->auth_key_kp.pubkey); @@ -1132,8 +1132,7 @@ test_closing_intro_circs(void *arg) /* Now pretend that a new intro point circ was launched and opened. Check * that the intro point will be established correctly. */ intro_circ = origin_circuit_init(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, flags); - intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk, - HS_IDENT_CIRCUIT_INTRO); + intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk); ed25519_pubkey_copy(&intro_circ->hs_ident->intro_auth_pk, &ip->auth_key_kp.pubkey); /* Register circuit in the circuitmap . */ @@ -1181,7 +1180,7 @@ test_introduce2(void *arg) MOCK(get_or_state, get_or_state_replacement); - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags); tt_assert(circ); @@ -1265,6 +1264,7 @@ test_service_event(void *arg) /* Set a service for this circuit. */ service = helper_create_service(); + tt_assert(service); ed25519_pubkey_copy(&circ->hs_ident->identity_pk, &service->keys.identity_pk); @@ -1300,8 +1300,14 @@ test_service_event(void *arg) run_housekeeping_event(now); tt_int_op(digest256map_size(service->desc_current->intro_points.map), OP_EQ, 1); + /* No removal if we have an established circuit after retries. */ + ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1; + run_housekeeping_event(now); + tt_int_op(digest256map_size(service->desc_current->intro_points.map), + OP_EQ, 1); /* Remove the IP object at once for the next test. */ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1; + ip->circuit_established = 0; run_housekeeping_event(now); tt_int_op(digest256map_size(service->desc_current->intro_points.map), OP_EQ, 0); @@ -1345,7 +1351,7 @@ test_rotate_descriptors(void *arg) (void) arg; - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); hs_init(); MOCK(get_or_state, get_or_state_replacement); @@ -1462,7 +1468,7 @@ test_build_update_descriptors(void *arg) MOCK(networkstatus_get_live_consensus, mock_networkstatus_get_live_consensus); - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC", &mock_ns.valid_after); @@ -1693,7 +1699,7 @@ test_build_descriptors(void *arg) MOCK(networkstatus_get_live_consensus, mock_networkstatus_get_live_consensus); - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC", &mock_ns.valid_after); @@ -1794,7 +1800,7 @@ test_upload_descriptors(void *arg) MOCK(networkstatus_get_live_consensus, mock_networkstatus_get_live_consensus); - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &mock_ns.valid_after); diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c index 4a6d90d97e..104e973b1f 100644 --- a/src/test/test_introduce.c +++ b/src/test/test_introduce.c @@ -383,8 +383,10 @@ make_intro_from_plaintext( /* Output the cell */ *cell_out = cell; + cell = NULL; done: + tor_free(cell); return cell_len; } @@ -535,4 +537,3 @@ struct testcase_t introduce_tests[] = { INTRODUCE_LEGACY(late_parse_v3), END_OF_TESTCASES }; - diff --git a/src/test/test_logging.c b/src/test/test_logging.c index bb7018fe1c..203ce64e32 100644 --- a/src/test/test_logging.c +++ b/src/test/test_logging.c @@ -35,7 +35,7 @@ test_get_sigsafe_err_fds(void *arg) set_log_severity_config(LOG_WARN, LOG_ERR, &include_bug); set_log_severity_config(LOG_WARN, LOG_ERR, &no_bug); - no_bug.masks[0] &= ~(LD_BUG|LD_GENERAL); + no_bug.masks[SEVERITY_MASK_IDX(LOG_ERR)] &= ~(LD_BUG|LD_GENERAL); set_log_severity_config(LOG_INFO, LOG_NOTICE, &no_bug2); /* Add some logs; make sure the output is as expected. */ diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index 53ee799185..804e6c546a 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -21,6 +21,7 @@ #include "feature/nodelist/routerstatus_st.h" #include "test/test.h" +#include "test/log_test_helpers.h" #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> @@ -648,6 +649,41 @@ static const char MD_PARSE_TEST_DATA[] = "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n" "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n" "p6 allow 80\n" + /* Good 11: Normal, non-exit relay with ipv6 address */ + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n" + "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n" + "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "a [::1:2:3:4]:9090\n" + "a 18.0.0.1:9999\n" + "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n" + "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n" + /* Good 12: Normal, exit relay with ipv6 address */ + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n" + "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n" + "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "a [::1:2:3:4]:9090\n" + "a 18.0.0.1:9999\n" + "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n" + "p accept 20-23,43,53,79-81,88,110,143,194,220,389,443,464,531,543-544\n" + "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n" + /* Good 13: Normal, exit relay with only ipv6 exit policy */ + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n" + "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n" + "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "a [::1:2:3:4]:9090\n" + "a 18.0.0.1:9999\n" + "ntor-onion-key k2yFqTU2vzMCQDEiE/j9UcEHxKrXMLpB3IL0or09sik=\n" + "p6 accept 20-23,43,53,79-81,88,110,143,194,220,389,443,464,531,543-544\n" + "id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n" ; #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS ENABLE_GCC_WARNING(overlength-strings) @@ -665,7 +701,7 @@ test_md_parse(void *arg) smartlist_t *mds = microdescs_parse_from_string(MD_PARSE_TEST_DATA, NULL, 1, SAVED_NOWHERE, invalid); - tt_int_op(smartlist_len(mds), OP_EQ, 11); + tt_int_op(smartlist_len(mds), OP_EQ, 14); tt_int_op(smartlist_len(invalid), OP_EQ, 4); test_memeq_hex(smartlist_get(invalid,0), @@ -712,6 +748,21 @@ test_md_parse(void *arg) tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6); tt_int_op(md->ipv6_orport, OP_EQ, 9090); + md = smartlist_get(mds, 11); + tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6); + tt_int_op(md->ipv6_orport, OP_EQ, 9090); + tt_int_op(md->policy_is_reject_star, OP_EQ, 1); + + md = smartlist_get(mds, 12); + tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6); + tt_int_op(md->ipv6_orport, OP_EQ, 9090); + tt_int_op(md->policy_is_reject_star, OP_EQ, 0); + + md = smartlist_get(mds, 13); + tt_assert(tor_addr_family(&md->ipv6_addr) == AF_INET6); + tt_int_op(md->ipv6_orport, OP_EQ, 9090); + tt_int_op(md->policy_is_reject_star, OP_EQ, 0); + done: SMARTLIST_FOREACH(mds, microdesc_t *, mdsc, microdesc_free(mdsc)); smartlist_free(mds); @@ -720,6 +771,80 @@ test_md_parse(void *arg) tor_free(mem_op_hex_tmp); } +static void +test_md_parse_id_ed25519(void *arg) +{ + (void)arg; + + /* A correct MD with an ed25519 ID ... and an unspecified ID type, + * which is permitted. */ + const char GOOD_MD[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n" + "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n" + "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyBrZXk\n" + "id wumpus dodecahedron\n"; + + smartlist_t *mds = NULL; + const microdesc_t *md; + + mds = microdescs_parse_from_string(GOOD_MD, + NULL, 1, SAVED_NOWHERE, NULL); + tt_assert(mds); + tt_int_op(smartlist_len(mds), OP_EQ, 1); + md = smartlist_get(mds, 0); + tt_mem_op(md->ed25519_identity_pkey, OP_EQ, + "This isn't actually a public key", ED25519_PUBKEY_LEN); + SMARTLIST_FOREACH(mds, microdesc_t *, m, microdesc_free(m)); + smartlist_free(mds); + + /* As above, but ed25519 ID key appears twice. */ + const char DUPLICATE_KEY[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n" + "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n" + "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyBrZXk\n" + "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyBrZXk\n"; + + setup_capture_of_logs(LOG_WARN); + mds = microdescs_parse_from_string(DUPLICATE_KEY, + NULL, 1, SAVED_NOWHERE, NULL); + tt_assert(mds); + tt_int_op(smartlist_len(mds), OP_EQ, 0); // no entries. + expect_single_log_msg_containing("Extra ed25519 key"); + mock_clean_saved_logs(); + smartlist_free(mds); + + /* As above, but ed25519 ID key is invalid. */ + const char BOGUS_KEY[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM7uUtq5F6h63QNYIvC+4NcWaD0DjtnrOORZMkdpJhinXUOwce3cD5Dj\n" + "sgdN1wJpWpTQMXJ2DssfSgmOVXETP7qJuZyRprxalQhaEATMDNJA/66Ml1jSO9mZ\n" + "+8Xb7m/4q778lNtkSbsvMaYD2Dq6k2QQ3kMhr9z8oUtX0XA23+pfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "id ed25519 VGhpcyBpc24ndCBhY3R1YWxseSBhIHB1YmxpYyZZZZZZZZZZZ\n"; + + mds = microdescs_parse_from_string(BOGUS_KEY, + NULL, 1, SAVED_NOWHERE, NULL); + tt_assert(mds); + tt_int_op(smartlist_len(mds), OP_EQ, 0); // no entries. + expect_single_log_msg_containing("Bogus ed25519 key"); + + done: + if (mds) { + SMARTLIST_FOREACH(mds, microdesc_t *, m, microdesc_free(m)); + smartlist_free(mds); + } + teardown_capture_of_logs(); +} + static int mock_rgsbd_called = 0; static routerstatus_t *mock_rgsbd_val_a = NULL; static routerstatus_t *mock_rgsbd_val_b = NULL; @@ -853,6 +978,7 @@ struct testcase_t microdesc_tests[] = { { "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL }, { "generate", test_md_generate, 0, NULL, NULL }, { "parse", test_md_parse, 0, NULL, NULL }, + { "parse_id_ed25519", test_md_parse_id_ed25519, 0, NULL, NULL }, { "reject_cache", test_md_reject_cache, TT_FORK, NULL, NULL }, { "corrupt_desc", test_md_corrupt_desc, TT_FORK, NULL, NULL }, END_OF_TESTCASES diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index 8d6d3cb974..b8ca56bb81 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -10,11 +10,13 @@ #include "core/or/or.h" #include "lib/crypt_ops/crypto_rand.h" +#include "feature/nodelist/describe.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodefamily.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/torcert.h" +#include "core/or/extend_info_st.h" #include "feature/nodelist/microdesc_st.h" #include "feature/nodelist/networkstatus_st.h" #include "feature/nodelist/node_st.h" @@ -76,7 +78,7 @@ test_nodelist_node_get_verbose_nickname_not_named(void *arg) } /** A node should be considered a directory server if it has an open dirport - * of it accepts tunnelled directory requests. + * or it accepts tunnelled directory requests. */ static void test_nodelist_node_is_dir(void *arg) @@ -640,6 +642,610 @@ test_nodelist_nodefamily_canonicalize(void *arg) tor_free(c); } +/** format_node_description() should return + * "Fingerprint~Nickname at IPv4 and [IPv6]". + * The nickname and addresses are optional. + */ +static void +test_nodelist_format_node_description(void *arg) +{ + char mock_digest[DIGEST_LEN]; + char mock_nickname[MAX_NICKNAME_LEN+1]; + tor_addr_t mock_null_ip; + tor_addr_t mock_ipv4; + tor_addr_t mock_ipv6; + + char ndesc[NODE_DESC_BUF_LEN]; + const char *rv = NULL; + + (void) arg; + + /* Clear variables */ + memset(ndesc, 0, sizeof(ndesc)); + memset(mock_digest, 0, sizeof(mock_digest)); + memset(mock_nickname, 0, sizeof(mock_nickname)); + memset(&mock_null_ip, 0, sizeof(mock_null_ip)); + memset(&mock_ipv4, 0, sizeof(mock_ipv4)); + memset(&mock_ipv6, 0, sizeof(mock_ipv6)); + + /* Set variables */ + memcpy(mock_digest, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_digest)); + strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname)); + tor_addr_parse(&mock_ipv4, "111.222.233.244"); + tor_addr_parse(&mock_ipv6, "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* Test function with variables */ + rv = format_node_description(ndesc, + mock_digest, + NULL, + NULL, + 0); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + + /* format node description should use ~ because named is deprecated */ + rv = format_node_description(ndesc, + mock_digest, + mock_nickname, + NULL, + 0); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(ndesc, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~""TestOR7890123456789"); + + /* Try a null IP address, rather than NULL */ + rv = format_node_description(ndesc, + mock_digest, + mock_nickname, + &mock_null_ip, + 0); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(ndesc, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789"); + + /* Try some real IP addresses */ + rv = format_node_description(ndesc, + mock_digest, + NULL, + &mock_ipv4, + 0); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(ndesc, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA at 111.222.233.244"); + + rv = format_node_description(ndesc, + mock_digest, + mock_nickname, + &mock_ipv6, + 0); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(ndesc, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + rv = format_node_description(ndesc, + mock_digest, + mock_nickname, + &mock_ipv6, + tor_addr_to_ipv4h(&mock_ipv4)); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(ndesc, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* test NULL handling */ + rv = format_node_description(NULL, NULL, NULL, NULL, 0); + tt_str_op(rv, OP_EQ, "<NULL BUFFER>"); + + rv = format_node_description(ndesc, NULL, NULL, NULL, 0); + tt_ptr_op(rv, OP_EQ, ndesc); + tt_str_op(rv, OP_EQ, "<NULL ID DIGEST>"); + + done: + return; +} + +/** router_describe() is a wrapper for format_node_description(), see that + * test for details. + * + * The routerinfo-only node_describe() tests are in this function, + * so we can re-use the same mocked variables. + */ +static void +test_nodelist_router_describe(void *arg) +{ + char mock_nickname[MAX_NICKNAME_LEN+1]; + tor_addr_t mock_ipv4; + routerinfo_t mock_ri_ipv4; + routerinfo_t mock_ri_ipv6; + routerinfo_t mock_ri_dual; + + const char *rv = NULL; + + (void) arg; + + /* Clear variables */ + memset(mock_nickname, 0, sizeof(mock_nickname)); + memset(&mock_ipv4, 0, sizeof(mock_ipv4)); + memset(&mock_ri_ipv4, 0, sizeof(mock_ri_ipv4)); + memset(&mock_ri_ipv6, 0, sizeof(mock_ri_ipv6)); + memset(&mock_ri_dual, 0, sizeof(mock_ri_dual)); + + /* Set up the dual-stack routerinfo */ + memcpy(mock_ri_dual.cache_info.identity_digest, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_ri_dual.cache_info.identity_digest)); + strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname)); + mock_ri_dual.nickname = mock_nickname; + tor_addr_parse(&mock_ipv4, "111.222.233.244"); + mock_ri_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_ri_dual.ipv6_addr, + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* Create and modify the other routerinfos. + * mock_nickname is referenced from all 3 routerinfos. + * That's ok, all their memory is static. */ + memcpy(&mock_ri_ipv4, &mock_ri_dual, sizeof(mock_ri_ipv4)); + memcpy(&mock_ri_ipv6, &mock_ri_dual, sizeof(mock_ri_ipv6)); + /* Clear the unnecessary addresses */ + memset(&mock_ri_ipv4.ipv6_addr, 0, sizeof(mock_ri_ipv4.ipv6_addr)); + mock_ri_ipv6.addr = 0; + + /* We don't test the no-nickname and no-IP cases, because they're covered by + * format_node_description(), and we don't expect to see them in Tor code. */ + + /* Try some real IP addresses */ + rv = router_describe(&mock_ri_ipv4); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244"); + + rv = router_describe(&mock_ri_ipv6); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + rv = router_describe(&mock_ri_dual); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* test NULL handling */ + rv = router_describe(NULL); + tt_str_op(rv, OP_EQ, "<null>"); + + /* Now test a node with only these routerinfos */ + node_t mock_node; + memset(&mock_node, 0, sizeof(mock_node)); + memcpy(mock_node.identity, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_node.identity)); + + /* Try some real IP addresses */ + mock_node.ri = &mock_ri_ipv4; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244"); + + mock_node.ri = &mock_ri_ipv6; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + mock_node.ri = &mock_ri_dual; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); + + done: + return; +} + +/** node_describe() is a wrapper for format_node_description(), see that + * test for details. + * + * The routerinfo-only and routerstatus-only node_describe() tests are in + * test_nodelist_router_describe() and test_nodelist_routerstatus_describe(), + * so we can re-use their mocked variables. + */ +static void +test_nodelist_node_describe(void *arg) +{ + char mock_nickname[MAX_NICKNAME_LEN+1]; + tor_addr_t mock_ipv4; + + const char *rv = NULL; + + (void) arg; + + /* Routerinfos */ + routerinfo_t mock_ri_dual; + + /* Clear variables */ + memset(mock_nickname, 0, sizeof(mock_nickname)); + memset(&mock_ipv4, 0, sizeof(mock_ipv4)); + memset(&mock_ri_dual, 0, sizeof(mock_ri_dual)); + + /* Set up the dual-stack routerinfo */ + memcpy(mock_ri_dual.cache_info.identity_digest, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_ri_dual.cache_info.identity_digest)); + strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname)); + mock_ri_dual.nickname = mock_nickname; + tor_addr_parse(&mock_ipv4, "111.222.233.244"); + mock_ri_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_ri_dual.ipv6_addr, + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* Routerstatuses */ + routerstatus_t mock_rs_ipv4; + routerstatus_t mock_rs_dual; + + /* Clear variables */ + memset(&mock_ipv4, 0, sizeof(mock_ipv4)); + memset(&mock_rs_ipv4, 0, sizeof(mock_rs_ipv4)); + memset(&mock_rs_dual, 0, sizeof(mock_rs_dual)); + + /* Set up the dual-stack routerstatus */ + memcpy(mock_rs_dual.identity_digest, + "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB" + "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB", + sizeof(mock_rs_dual.identity_digest)); + strlcpy(mock_rs_dual.nickname, "Bbb", + sizeof(mock_rs_dual.nickname)); + tor_addr_parse(&mock_ipv4, "2.2.2.2"); + mock_rs_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_rs_dual.ipv6_addr, + "[bbbb::bbbb]"); + + /* Create and modify the other routerstatus. */ + memcpy(&mock_rs_ipv4, &mock_rs_dual, sizeof(mock_rs_ipv4)); + /* Clear the unnecessary IPv6 address */ + memset(&mock_rs_ipv4.ipv6_addr, 0, sizeof(mock_rs_ipv4.ipv6_addr)); + + /* Microdescs */ + microdesc_t mock_md_null; + microdesc_t mock_md_ipv6; + + /* Clear variables */ + memset(&mock_md_null, 0, sizeof(mock_md_null)); + memset(&mock_md_ipv6, 0, sizeof(mock_md_ipv6)); + + /* Set up the microdesc */ + tor_addr_parse(&mock_md_ipv6.ipv6_addr, + "[eeee::6000:6000]"); + + /* Set up the node */ + node_t mock_node; + memset(&mock_node, 0, sizeof(mock_node)); + memcpy(mock_node.identity, + "\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC" + "\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC", + sizeof(mock_node.identity)); + + /* Test that the routerinfo and routerstatus work separately, but the + * identity comes from the node */ + mock_node.ri = &mock_ri_dual; + mock_node.rs = NULL; + mock_node.md = NULL; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~TestOR7890123456789 at " + "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); + + mock_node.ri = NULL; + mock_node.rs = &mock_rs_ipv4; + mock_node.md = NULL; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2"); + + mock_node.ri = NULL; + mock_node.rs = &mock_rs_dual; + mock_node.md = NULL; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [bbbb::bbbb]"); + + /* Test that the routerstatus overrides the routerinfo */ + mock_node.ri = &mock_ri_dual; + mock_node.rs = &mock_rs_ipv4; + mock_node.md = NULL; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2"); + + mock_node.ri = &mock_ri_dual; + mock_node.rs = &mock_rs_dual; + mock_node.md = NULL; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [bbbb::bbbb]"); + + /* Test that the microdesc IPv6 is used if the routerinfo doesn't have IPv6 + */ + mock_node.ri = NULL; + mock_node.rs = &mock_rs_ipv4; + mock_node.md = &mock_md_ipv6; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [eeee::6000:6000]"); + + mock_node.ri = NULL; + mock_node.rs = &mock_rs_ipv4; + mock_node.md = &mock_md_null; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2"); + + mock_node.ri = NULL; + mock_node.rs = &mock_rs_dual; + mock_node.md = &mock_md_ipv6; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [bbbb::bbbb]"); + + mock_node.ri = NULL; + mock_node.rs = &mock_rs_dual; + mock_node.md = &mock_md_null; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [bbbb::bbbb]"); + + /* Test that the routerinfo doesn't change the results above + */ + mock_node.ri = &mock_ri_dual; + mock_node.rs = &mock_rs_ipv4; + mock_node.md = &mock_md_ipv6; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [eeee::6000:6000]"); + + mock_node.ri = &mock_ri_dual; + mock_node.rs = &mock_rs_ipv4; + mock_node.md = &mock_md_null; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2"); + + mock_node.ri = &mock_ri_dual; + mock_node.rs = &mock_rs_dual; + mock_node.md = &mock_md_ipv6; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [bbbb::bbbb]"); + + mock_node.ri = &mock_ri_dual; + mock_node.rs = &mock_rs_dual; + mock_node.md = &mock_md_null; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~Bbb at " + "2.2.2.2 and [bbbb::bbbb]"); + + /* test NULL handling */ + rv = node_describe(NULL); + tt_str_op(rv, OP_EQ, "<null>"); + + mock_node.ri = NULL; + mock_node.rs = NULL; + mock_node.md = NULL; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "<null rs and ri>"); + + done: + return; +} + +/** routerstatus_describe() is a wrapper for format_node_description(), see + * that test for details. + * + * The routerstatus-only node_describe() tests are in this function, + * so we can re-use the same mocked variables. + */ +static void +test_nodelist_routerstatus_describe(void *arg) +{ + tor_addr_t mock_ipv4; + routerstatus_t mock_rs_ipv4; + routerstatus_t mock_rs_ipv6; + routerstatus_t mock_rs_dual; + + const char *rv = NULL; + + (void) arg; + + /* Clear variables */ + memset(&mock_ipv4, 0, sizeof(mock_ipv4)); + memset(&mock_rs_ipv4, 0, sizeof(mock_rs_ipv4)); + memset(&mock_rs_ipv6, 0, sizeof(mock_rs_ipv6)); + memset(&mock_rs_dual, 0, sizeof(mock_rs_dual)); + + /* Set up the dual-stack routerstatus */ + memcpy(mock_rs_dual.identity_digest, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_rs_dual.identity_digest)); + strlcpy(mock_rs_dual.nickname, "TestOR7890123456789", + sizeof(mock_rs_dual.nickname)); + tor_addr_parse(&mock_ipv4, "111.222.233.244"); + mock_rs_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_rs_dual.ipv6_addr, + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* Create and modify the other routerstatuses. */ + memcpy(&mock_rs_ipv4, &mock_rs_dual, sizeof(mock_rs_ipv4)); + memcpy(&mock_rs_ipv6, &mock_rs_dual, sizeof(mock_rs_ipv6)); + /* Clear the unnecessary addresses */ + memset(&mock_rs_ipv4.ipv6_addr, 0, sizeof(mock_rs_ipv4.ipv6_addr)); + mock_rs_ipv6.addr = 0; + + /* We don't test the no-nickname and no-IP cases, because they're covered by + * format_node_description(), and we don't expect to see them in Tor code. */ + + /* Try some real IP addresses */ + rv = routerstatus_describe(&mock_rs_ipv4); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244"); + + rv = routerstatus_describe(&mock_rs_ipv6); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + rv = routerstatus_describe(&mock_rs_dual); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* test NULL handling */ + rv = routerstatus_describe(NULL); + tt_str_op(rv, OP_EQ, "<null>"); + + /* Now test a node with only these routerstatuses */ + node_t mock_node; + memset(&mock_node, 0, sizeof(mock_node)); + memcpy(mock_node.identity, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_node.identity)); + + /* Try some real IP addresses */ + mock_node.rs = &mock_rs_ipv4; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244"); + + mock_node.rs = &mock_rs_ipv6; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + mock_node.rs = &mock_rs_dual; + rv = node_describe(&mock_node); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); + + done: + return; +} + +/** extend_info_describe() is a wrapper for format_node_description(), see + * that test for details. + */ +static void +test_nodelist_extend_info_describe(void *arg) +{ + extend_info_t mock_ei_ipv4; + extend_info_t mock_ei_ipv6; + + const char *rv = NULL; + + (void) arg; + + /* Clear variables */ + memset(&mock_ei_ipv4, 0, sizeof(mock_ei_ipv4)); + memset(&mock_ei_ipv6, 0, sizeof(mock_ei_ipv6)); + + /* Set up the IPv4 extend info */ + memcpy(mock_ei_ipv4.identity_digest, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + sizeof(mock_ei_ipv4.identity_digest)); + strlcpy(mock_ei_ipv4.nickname, "TestOR7890123456789", + sizeof(mock_ei_ipv4.nickname)); + tor_addr_parse(&mock_ei_ipv4.addr, "111.222.233.244"); + + /* Create and modify the other extend info. */ + memcpy(&mock_ei_ipv6, &mock_ei_ipv4, sizeof(mock_ei_ipv6)); + tor_addr_parse(&mock_ei_ipv6.addr, + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* We don't test the no-nickname and no-IP cases, because they're covered by + * format_node_description(), and we don't expect to see them in Tor code. */ + + /* Try some real IP addresses */ + rv = extend_info_describe(&mock_ei_ipv4); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "111.222.233.244"); + + rv = extend_info_describe(&mock_ei_ipv6); + tt_str_op(rv, OP_EQ, + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " + "[1111:2222:3333:4444:5555:6666:7777:8888]"); + + /* Extend infos only have one IP address, so there is no dual case */ + + /* test NULL handling */ + rv = extend_info_describe(NULL); + tt_str_op(rv, OP_EQ, "<null>"); + + done: + return; +} + +/** router_get_verbose_nickname() should return "Fingerprint~Nickname" + */ +static void +test_nodelist_router_get_verbose_nickname(void *arg) +{ + routerinfo_t mock_ri; + char mock_nickname[MAX_NICKNAME_LEN+1]; + + char vname[MAX_VERBOSE_NICKNAME_LEN+1]; + + (void) arg; + + memset(&mock_ri, 0, sizeof(routerinfo_t)); + memset(mock_nickname, 0, sizeof(mock_nickname)); + mock_ri.nickname = mock_nickname; + + /* verbose nickname should use ~ because named is deprecated */ + strlcpy(mock_nickname, "TestOR", sizeof(mock_nickname)); + memcpy(mock_ri.cache_info.identity_digest, + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + DIGEST_LEN); + router_get_verbose_nickname(vname, &mock_ri); + tt_str_op(vname, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR"); + + /* test NULL router handling */ + router_get_verbose_nickname(vname, NULL); + tt_str_op(vname, OP_EQ, "<null>"); + + router_get_verbose_nickname(NULL, &mock_ri); + router_get_verbose_nickname(NULL, NULL); + + done: + return; +} + #define NODE(name, flags) \ { #name, test_nodelist_##name, (flags), NULL, NULL } @@ -654,5 +1260,11 @@ struct testcase_t nodelist_tests[] = { NODE(nickname_matches, 0), NODE(node_nodefamily, TT_FORK), NODE(nodefamily_canonicalize, 0), + NODE(format_node_description, 0), + NODE(router_describe, 0), + NODE(node_describe, 0), + NODE(routerstatus_describe, 0), + NODE(extend_info_describe, 0), + NODE(router_get_verbose_nickname, 0), END_OF_TESTCASES }; diff --git a/src/test/test_options.c b/src/test/test_options.c index 7009910b0f..69407a999b 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -5,7 +5,7 @@ #define CONFIG_PRIVATE #include "core/or/or.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" #include "app/config/config.h" #include "test/test.h" #include "lib/geoip/geoip.h" @@ -14,6 +14,7 @@ #include "feature/nodelist/routerset.h" #include "core/mainloop/mainloop.h" #include "test/log_test_helpers.h" +#include "test/resolve_test_helpers.h" #include "lib/sandbox/sandbox.h" #include "lib/memarea/memarea.h" @@ -54,9 +55,9 @@ setup_log_callback(void) { log_severity_list_t lst; memset(&lst, 0, sizeof(lst)); - lst.masks[LOG_ERR - LOG_ERR] = ~0; - lst.masks[LOG_WARN - LOG_ERR] = ~0; - lst.masks[LOG_NOTICE - LOG_ERR] = ~0; + lst.masks[SEVERITY_MASK_IDX(LOG_ERR)] = LD_ALL_DOMAINS; + lst.masks[SEVERITY_MASK_IDX(LOG_WARN)] = LD_ALL_DOMAINS; + lst.masks[SEVERITY_MASK_IDX(LOG_NOTICE)] = LD_ALL_DOMAINS; add_callback_log(&lst, log_cback); mark_logs_temp(); } @@ -96,7 +97,7 @@ clear_log_messages(void) opt->command = CMD_RUN_TOR; \ options_init(opt); \ \ - dflt = config_dup(&options_format, opt); \ + dflt = config_dup(get_options_mgr(), opt); \ clear_log_messages(); \ } while (0) @@ -196,7 +197,7 @@ test_options_validate_impl(const char *configuration, if (r) goto done; - r = config_assign(&options_format, opt, cl, 0, &msg); + r = config_assign(get_options_mgr(), opt, cl, 0, &msg); if (phase == PH_ASSIGN) { if (test_options_checkmsgs(configuration, expect_errmsg, expect_log_severity, @@ -241,6 +242,7 @@ test_options_validate(void *arg) (void)arg; setup_log_callback(); sandbox_disable_getaddrinfo_cache(); + mock_hostname_resolver(); WANT_ERR("ExtORPort 500000", "Invalid ExtORPort", PH_VALIDATE); @@ -258,13 +260,17 @@ test_options_validate(void *arg) WANT_ERR("BridgeRelay 1\nDirCache 0", "We're a bridge but DirCache is disabled.", PH_VALIDATE); + // XXXX We should replace this with a more full error message once #29211 + // XXXX is done. It is truncated for now because at the current stage + // XXXX of refactoring, we can't give a full error message like before. WANT_ERR_LOG("HeartbeatPeriod 21 snarks", - "Interval 'HeartbeatPeriod 21 snarks' is malformed or" - " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + "malformed or out of bounds", LOG_WARN, + "Unknown unit 'snarks'.", PH_ASSIGN); + // XXXX As above. WANT_ERR_LOG("LogTimeGranularity 21 snarks", - "Msec interval 'LogTimeGranularity 21 snarks' is malformed or" - " out of bounds.", LOG_WARN, "Unknown unit 'snarks'.", + "malformed or out of bounds", LOG_WARN, + "Unknown unit 'snarks'.", PH_ASSIGN); OK("HeartbeatPeriod 1 hour", PH_VALIDATE); OK("LogTimeGranularity 100 milliseconds", PH_VALIDATE); @@ -278,6 +284,7 @@ test_options_validate(void *arg) close_temp_logs(); clear_log_messages(); + unmock_hostname_resolver(); return; } @@ -300,7 +307,7 @@ test_have_enough_mem_for_dircache(void *arg) r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, &msg); + r = config_assign(get_options_mgr(), opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); /* 300 MB RAM available, DirCache enabled */ @@ -323,7 +330,7 @@ test_have_enough_mem_for_dircache(void *arg) r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, &msg); + r = config_assign(get_options_mgr(), opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); /* 300 MB RAM available, DirCache enabled, Bridge */ @@ -346,7 +353,7 @@ test_have_enough_mem_for_dircache(void *arg) r = config_get_lines(configuration, &cl, 1); tt_int_op(r, OP_EQ, 0); - r = config_assign(&options_format, opt, cl, 0, &msg); + r = config_assign(get_options_mgr(), opt, cl, 0, &msg); tt_int_op(r, OP_EQ, 0); /* 200 MB RAM available, DirCache disabled */ @@ -434,7 +441,7 @@ get_options_test_data(const char *conf) rv = config_get_lines(conf, &cl, 1); tt_int_op(rv, OP_EQ, 0); - rv = config_assign(&options_format, result->opt, cl, 0, &msg); + rv = config_assign(get_options_mgr(), result->opt, cl, 0, &msg); if (msg) { /* Display the parse error message by comparing it with an empty string */ tt_str_op(msg, OP_EQ, ""); @@ -445,7 +452,7 @@ get_options_test_data(const char *conf) result->opt->TokenBucketRefillInterval = 1; rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1); tt_int_op(rv, OP_EQ, 0); - rv = config_assign(&options_format, result->def_opt, cl, 0, &msg); + rv = config_assign(get_options_mgr(), result->def_opt, cl, 0, &msg); if (msg) { /* Display the parse error message by comparing it with an empty string */ tt_str_op(msg, OP_EQ, ""); @@ -1168,7 +1175,7 @@ test_options_validate__transproxy(void *ignored) // Assert that a test has run for some TransProxyType tt_assert(tdata); -#else /* !(defined(USE_TRANSPARENT)) */ +#else /* !defined(USE_TRANSPARENT) */ tdata = get_options_test_data("TransPort 127.0.0.1:555\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); @@ -1343,29 +1350,6 @@ test_options_validate__token_bucket(void *ignored) } static void -test_options_validate__recommended_packages(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - setup_capture_of_logs(LOG_WARN); - options_test_data_t *tdata = get_options_test_data( - "RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n" - "RecommendedPackages invalid-package-line\n"); - - ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); - tt_int_op(ret, OP_EQ, -1); - expect_no_log_msg("Invalid RecommendedPackage line " - "invalid-package-line will be ignored\n"); - - done: - escaped(NULL); // This will free the leaking memory from the previous escaped - teardown_capture_of_logs(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__fetch_dir(void *ignored) { (void)ignored; @@ -4200,7 +4184,6 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(exclude_nodes), LOCAL_VALIDATE_TEST(node_families), LOCAL_VALIDATE_TEST(token_bucket), - LOCAL_VALIDATE_TEST(recommended_packages), LOCAL_VALIDATE_TEST(fetch_dir), LOCAL_VALIDATE_TEST(conn_limit), LOCAL_VALIDATE_TEST(paths_needed), diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh new file mode 100755 index 0000000000..eeb80cdfa7 --- /dev/null +++ b/src/test/test_parseconf.sh @@ -0,0 +1,204 @@ +#!/bin/sh +# Copyright 2019, The Tor Project, Inc. +# See LICENSE for licensing information + +# Integration test script for verifying that Tor configurations are parsed as +# we expect. +# +# Valid configurations are tested with --dump-config, which parses and +# validates the configuration before writing it out. We then make sure that +# the result is what we expect, before parsing and dumping it again to make +# sure that there is no change. +# +# Invalid configurations are tested with --verify-config, which parses +# and validates the configuration. We capture its output and make sure that +# it contains the error message we expect. + +# This script looks for its test cases as individual directories in +# src/test/conf_examples/. Each test may have these files: +# +# torrc -- Usually needed. This file is passed to Tor on the command line +# with the "-f" flag. (If you omit it, you'll test Tor's behavior when +# it receives a nonexistent configuration file.) +# +# torrc.defaults -- Optional. If present, it is passed to Tor on the command +# line with the --defaults-torrc option. If this file is absent, an empty +# file is passed instead to prevent Tor from reading the system defaults. +# +# cmdline -- Optional. If present, it contains command-line arguments that +# will be passed to Tor. +# +# expected -- If this file is present, then it should be the expected result +# of "--dump-config short" for this test case. Exactly one of +# "expected" or "error" must be present, or the test will fail. +# +# error -- If this file is present, then it contains a regex that must be +# matched by some line in the output of "--verify-config", which must +# fail. Exactly one of "expected" or "error" must be present, or the +# test will fail. + +umask 077 +set -e + +# emulate realpath(), in case coreutils or equivalent is not installed. +abspath() { + f="$*" + if [ -d "$f" ]; then + dir="$f" + base="" + else + dir="$(dirname "$f")" + base="/$(basename "$f")" + fi + dir="$(cd "$dir" && pwd)" + echo "$dir$base" +} + +# find the tor binary +if [ $# -ge 1 ]; then + TOR_BINARY="${1}" + shift +else + TOR_BINARY="${TESTING_TOR_BINARY:-./src/app/tor}" +fi + +TOR_BINARY="$(abspath "$TOR_BINARY")" + +# make a safe space for temporary files +DATA_DIR=$(mktemp -d -t tor_parseconf_tests.XXXXXX) +trap 'rm -rf "$DATA_DIR"' 0 + +# This is where we look for examples +EXAMPLEDIR="$(dirname "$0")"/conf_examples + +case "$(uname -s)" in + CYGWIN*) WINDOWS=1;; + MINGW*) WINDOWS=1;; + MSYS*) WINDOWS=1;; + *) WINDOWS=0;; +esac + +#### +# BUG WORKAROUND FOR 31757: +# On Appveyor, it seems that Tor sometimes randomly fails to produce +# output with --dump-config. Whil we are figuring this out, do not treat +# windows errors as hard failures. +#### +if test "$WINDOWS" = 1; then + EXITCODE=0 +else + EXITCODE=1 +fi + +die() { echo "$1" >&2 ; exit "$EXITCODE"; } + +if test "$WINDOWS" = 1; then + FILTER="dos2unix" +else + FILTER="cat" +fi + +touch "${DATA_DIR}/EMPTY" || die "Couldn't create empty file." + +for dir in "${EXAMPLEDIR}"/*; do + if ! test -d "${dir}"; then + # Only count directories. + continue + fi + + testname="$(basename "${dir}")" + # We use printf since "echo -n" is not standard + printf "%s: " "$testname" + + PREV_DIR="$(pwd)" + cd "${dir}" + + if test -f "./torrc.defaults"; then + DEFAULTS="./torrc.defaults" + else + DEFAULTS="${DATA_DIR}/EMPTY" + fi + + if test -f "./cmdline"; then + CMDLINE="$(cat ./cmdline)" + else + CMDLINE="" + fi + + if test -f "./expected"; then + if test -f "./error"; then + echo "FAIL: Found both ${dir}/expected and ${dir}/error." + echo "(Only one of these files should exist.)" + exit $EXITCODE + fi + + # This case should succeed: run dump-config and see if it does. + + "${TOR_BINARY}" -f "./torrc" \ + --defaults-torrc "${DEFAULTS}" \ + --dump-config short \ + ${CMDLINE} \ + | "${FILTER}" > "${DATA_DIR}/output.${testname}" \ + || die "Failure: Tor exited." + + if cmp "./expected" "${DATA_DIR}/output.${testname}">/dev/null ; then + # Check round-trip. + "${TOR_BINARY}" -f "${DATA_DIR}/output.${testname}" \ + --defaults-torrc "${DATA_DIR}/empty" \ + --dump-config short \ + | "${FILTER}" \ + > "${DATA_DIR}/output_2.${testname}" \ + || die "Failure: Tor exited on round-trip." + + if ! cmp "${DATA_DIR}/output.${testname}" \ + "${DATA_DIR}/output_2.${testname}"; then + echo "Failure: did not match on round-trip." + exit $EXITCODE + fi + + echo "OK" + else + echo "FAIL" + if test "$(wc -c < "${DATA_DIR}/output.${testname}")" = 0; then + # There was no output -- probably we failed. + "${TOR_BINARY}" -f "./torrc" \ + --defaults-torrc "${DEFAULTS}" \ + --verify-config \ + ${CMDLINE} || true + fi + diff -u "./expected" "${DATA_DIR}/output.${testname}" || /bin/true + exit $EXITCODE + fi + + elif test -f "./error"; then + # This case should fail: run verify-config and see if it does. + + "${TOR_BINARY}" --verify-config \ + -f ./torrc \ + --defaults-torrc "${DEFAULTS}" \ + ${CMDLINE} \ + > "${DATA_DIR}/output.${testname}" \ + && die "Failure: Tor did not report an error." + + expect_err="$(cat ./error)" + if grep "${expect_err}" "${DATA_DIR}/output.${testname}" >/dev/null; then + echo "OK" + else + echo "FAIL" + echo "Expected error: ${expect_err}" + echo "Tor said:" + cat "${DATA_DIR}/output.${testname}" + exit $EXITCODE + fi + + else + # This case is not actually configured with a success or a failure. + # call that an error. + + echo "FAIL: Did not find ${dir}/expected or ${dir}/error." + exit $EXITCODE + fi + + cd "${PREV_DIR}" + +done diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c new file mode 100644 index 0000000000..7dc9abde31 --- /dev/null +++ b/src/test/test_process_descs.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#include "core/or/or.h" +#include "feature/dirauth/process_descs.h" + +#include "test/test.h" + +static void +test_process_descs_versions(void *arg) +{ + (void)arg; + struct { + const char *version; + bool should_reject; + } cases[] = { + // a very old version: reject. + { "Tor 0.1.2.3-alpha", true }, + // a non-tor program: don't reject. + { "Wombat 0.1.2.3-alpha", false }, + // a slightly old version: reject + { "Tor 0.2.9.4-alpha", true }, + // a slightly old version: just new enough to support. + { "Tor 0.2.9.5-alpha", false }, + // a newer 0.2.9 version: supported. + { "Tor 0.2.9.100", false }, + // some unsupported versions: reject. + { "Tor 0.3.0.0-alpha-dev", true }, + { "Tor 0.3.0.2-alpha", true }, + { "Tor 0.3.0.5", true }, + { "Tor 0.3.1.4", true }, + { "Tor 0.3.2.4", true }, + { "Tor 0.3.3.4", true }, + { "Tor 0.3.4.1-alpha", true }, + { "Tor 0.3.4.100", true }, + { "Tor 0.3.5.1-alpha", true }, + { "Tor 0.3.5.6-rc", true}, + // new enough to be supported + { "Tor 0.3.5.7", false }, + { "Tor 0.3.5.8", false }, + { "Tor 0.4.0.1-alpha", false }, + { "Tor 0.4.1.5", false }, + // Very far in the future + { "Tor 100.100.1.5", false }, + }; + size_t n_cases = ARRAY_LENGTH(cases); + + for (unsigned i = 0; i < n_cases; ++i) { + const char *msg = NULL; + bool rejected = dirserv_rejects_tor_version(cases[i].version, &msg); + tt_int_op(rejected, OP_EQ, cases[i].should_reject); + tt_int_op(msg == NULL, OP_EQ, rejected == false); + } + + done: + ; +} + +#define T(name,flags) \ + { #name, test_process_descs_##name, (flags), NULL, NULL } + +struct testcase_t process_descs_tests[] = { + T(versions,0), + END_OF_TESTCASES +}; diff --git a/src/test/test_protover.c b/src/test/test_protover.c index 1759aef97d..0254501165 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -22,7 +22,7 @@ test_protover_parse(void *arg) tt_skip(); done: ; -#else /* !(defined(HAVE_RUST)) */ +#else /* !defined(HAVE_RUST) */ char *re_encoded = NULL; const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900"; diff --git a/src/test/test_pt.c b/src/test/test_pt.c index 87e3ba356c..8f3ce03c42 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -11,7 +11,7 @@ #define PROCESS_PRIVATE #include "core/or/or.h" #include "app/config/config.h" -#include "app/config/confparse.h" +#include "lib/confmgt/confparse.h" #include "feature/control/control.h" #include "feature/control/control_events.h" #include "feature/client/transports.h" @@ -352,7 +352,7 @@ test_pt_configure_proxy(void *arg) managed_proxy_t *mp = NULL; (void) arg; - dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_state = or_state_new(); MOCK(process_read_stdout, process_read_stdout_replacement); MOCK(get_or_state, diff --git a/src/test/test_pubsub_build.c b/src/test/test_pubsub_build.c index ce5bf60080..021323fbf1 100644 --- a/src/test/test_pubsub_build.c +++ b/src/test/test_pubsub_build.c @@ -493,48 +493,6 @@ test_pubsub_build_sub_many(void *arg) tor_free(sysname); } -/* The same subsystem can only declare one publish or subscribe. */ -static void -test_pubsub_build_pubsub_redundant(void *arg) -{ - (void)arg; - pubsub_builder_t *b = NULL; - dispatch_t *dispatcher = NULL; - pubsub_connector_t *c = NULL; - - b = pubsub_builder_new(); - seed_pubsub_builder_basic(b); - pub_binding_t btmp; - - { - c = pubsub_connector_for_subsystem(b, get_subsys_id("sys2")); - DISPATCH_ADD_SUB(c, main, bunch_of_coconuts); - pubsub_add_pub_(c, &btmp, get_channel_id("main"), - get_message_id("yes_we_have_no"), - get_msg_type_id("string"), - 0 /* flags */, - "somewhere.c", 22); - pubsub_connector_free(c); - }; - - setup_full_capture_of_logs(LOG_WARN); - dispatcher = pubsub_builder_finalize(b, NULL); - b = NULL; - tt_assert(dispatcher == NULL); - - expect_log_msg_containing( - "Message \"yes_we_have_no\" is configured to be published by " - "subsystem \"sys2\" more than once."); - expect_log_msg_containing( - "Message \"bunch_of_coconuts\" is configured to be subscribed by " - "subsystem \"sys2\" more than once."); - - done: - pubsub_builder_free(b); - dispatch_free(dispatcher); - teardown_capture_of_logs(); -} - /* It's fine to declare the excl flag. */ static void test_pubsub_build_excl_ok(void *arg) @@ -614,7 +572,6 @@ struct testcase_t pubsub_build_tests[] = { T(pubsub_same, TT_FORK), T(pubsub_multi, TT_FORK), T(sub_many, TT_FORK), - T(pubsub_redundant, TT_FORK), T(excl_ok, TT_FORK), T(excl_bad, TT_FORK), END_OF_TESTCASES diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh index e0d8394d38..d6d9d86668 100755 --- a/src/test/test_rebind.sh +++ b/src/test/test_rebind.sh @@ -12,8 +12,6 @@ if test "$UNAME_OS" = 'CYGWIN' || \ fi fi -exitcode=0 - tmpdir= clean () { if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c index baddf8d66e..19483713f7 100644 --- a/src/test/test_switch_id.c +++ b/src/test/test_switch_id.c @@ -87,7 +87,7 @@ main(int argc, char **argv) fprintf(stderr, "This test is not supported on your OS.\n"); return 77; -#else /* !(defined(_WIN32)) */ +#else /* !defined(_WIN32) */ const char *username; const char *testname; if (argc != 3) { diff --git a/src/test/test_token_bucket.c b/src/test/test_token_bucket.c new file mode 100644 index 0000000000..31670718d9 --- /dev/null +++ b/src/test/test_token_bucket.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_bwmgt.c + * \brief tests for bandwidth management / token bucket functions + */ + +#define TOKEN_BUCKET_PRIVATE + +#include "core/or/or.h" +#include "test/test.h" + +#include "lib/evloop/token_bucket.h" + +// an imaginary time, in timestamp units. Chosen so it will roll over. +static const uint32_t START_TS = UINT32_MAX - 1000; +static const uint32_t RATE = 10; +static const uint32_t BURST = 50; + +static void +test_token_bucket_ctr_init(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST); + tt_uint_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS); + tt_int_op(tb.counter.bucket, OP_EQ, BURST); + + done: + ; +} + +static void +test_token_bucket_ctr_adjust(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Increase burst. */ + token_bucket_ctr_adjust(&tb, RATE, BURST * 2); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST * 2); + + /* Decrease burst but still above bucket value. */ + token_bucket_ctr_adjust(&tb, RATE, BURST + 10); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST + 10); + + /* Decrease burst below bucket value. */ + token_bucket_ctr_adjust(&tb, RATE, BURST - 1); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST - 1); + + /* Change rate. */ + token_bucket_ctr_adjust(&tb, RATE * 2, BURST); + tt_uint_op(tb.cfg.rate, OP_EQ, RATE * 2); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + tt_uint_op(tb.cfg.burst, OP_EQ, BURST); + + done: + ; +} + +static void +test_token_bucket_ctr_dec(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Simple decrement by one. */ + tt_uint_op(0, OP_EQ, token_bucket_ctr_dec(&tb, 1)); + tt_uint_op(tb.counter.bucket, OP_EQ, BURST - 1); + + /* Down to 0. Becomes empty. */ + tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST - 1)); + tt_uint_op(tb.counter.bucket, OP_EQ, 0); + + /* Reset and try to underflow. */ + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + tt_uint_op(true, OP_EQ, token_bucket_ctr_dec(&tb, BURST + 1)); + tt_int_op(tb.counter.bucket, OP_EQ, -1); + + /* Keep underflowing shouldn't flag the bucket as empty. */ + tt_uint_op(false, OP_EQ, token_bucket_ctr_dec(&tb, BURST)); + tt_int_op(tb.counter.bucket, OP_EQ, - (int32_t) (BURST + 1)); + + done: + ; +} + +static void +test_token_bucket_ctr_refill(void *arg) +{ + (void) arg; + token_bucket_ctr_t tb; + + token_bucket_ctr_init(&tb, RATE, BURST, START_TS); + + /* Reduce of half the bucket and let a single second go before refill. */ + token_bucket_ctr_dec(&tb, BURST / 2); + tt_int_op(tb.counter.bucket, OP_EQ, BURST / 2); + token_bucket_ctr_refill(&tb, START_TS + 1); + tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1); + + /* No time change, nothing should move. */ + token_bucket_ctr_refill(&tb, START_TS + 1); + tt_int_op(tb.counter.bucket, OP_EQ, (BURST / 2) + RATE); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 1); + + /* Add 99 seconds, bucket should be back to a full BURST. */ + token_bucket_ctr_refill(&tb, START_TS + 99); + tt_int_op(tb.counter.bucket, OP_EQ, BURST); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 99); + + /* Empty bucket at once. */ + token_bucket_ctr_dec(&tb, BURST); + tt_int_op(tb.counter.bucket, OP_EQ, 0); + /* On second passes. */ + token_bucket_ctr_refill(&tb, START_TS + 100); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 100); + tt_int_op(tb.counter.bucket, OP_EQ, RATE); + /* A second second passes. */ + token_bucket_ctr_refill(&tb, START_TS + 101); + tt_int_op(tb.last_refilled_at_timestamp, OP_EQ, START_TS + 101); + tt_int_op(tb.counter.bucket, OP_EQ, RATE * 2); + + done: + ; +} + +#define TOKEN_BUCKET(name) \ + { #name, test_token_bucket_ ## name , 0, NULL, NULL } + +struct testcase_t token_bucket_tests[] = { + TOKEN_BUCKET(ctr_init), + TOKEN_BUCKET(ctr_adjust), + TOKEN_BUCKET(ctr_dec), + TOKEN_BUCKET(ctr_refill), + END_OF_TESTCASES +}; diff --git a/src/test/test_util.c b/src/test/test_util.c index b4d8a4d767..aebefe64c5 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5399,6 +5399,13 @@ test_util_socketpair(void *arg) tt_skip(); } #endif /* defined(__FreeBSD__) */ +#ifdef ENETUNREACH + if (ersatz && socketpair_result == -ENETUNREACH) { + /* We can also fail with -ENETUNREACH if we have no network stack at + * all. */ + tt_skip(); + } +#endif /* defined(ENETUNREACH) */ tt_int_op(0, OP_EQ, socketpair_result); tt_assert(SOCKET_OK(fds[0])); @@ -6116,7 +6123,7 @@ test_util_log_mallinfo(void *arg) } else { tt_u64_op(mem1, OP_LT, mem2); } -#else /* !(defined(HAVE_MALLINFO)) */ +#else /* !defined(HAVE_MALLINFO) */ tt_skip(); #endif /* defined(HAVE_MALLINFO) */ done: @@ -6175,7 +6182,7 @@ test_util_map_anon_nofork(void *arg) tt_skip(); done: ; -#else /* !(defined(_WIN32)) */ +#else /* !defined(_WIN32) */ /* We have the right OS support. We're going to try marking the buffer as * either zero-on-fork or as drop-on-fork, whichever is supported. Then we * will fork and send a byte back to the parent process. This will either diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c index c58634da5c..ba478a45a4 100644 --- a/src/test/test_workqueue.c +++ b/src/test/test_workqueue.c @@ -63,7 +63,7 @@ mark_handled(int serial) tor_assert(! bitarray_is_set(handled, serial)); bitarray_set(handled, serial); tor_mutex_release(&bitmap_mutex); -#else /* !(defined(TRACK_RESPONSES)) */ +#else /* !defined(TRACK_RESPONSES) */ (void)serial; #endif /* defined(TRACK_RESPONSES) */ } diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 6137def5a8..06a6f312ad 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -89,7 +89,7 @@ setup_directory(void) (int)getpid(), rnd32); r = mkdir(temp_dir); } -#else /* !(defined(_WIN32)) */ +#else /* !defined(_WIN32) */ tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s", (int) getpid(), rnd32); r = mkdir(temp_dir, 0700); @@ -295,7 +295,7 @@ main(int c, const char **v) memset(&s, 0, sizeof(s)); set_log_severity_config(loglevel, LOG_ERR, &s); /* ALWAYS log bug warnings. */ - s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; + s.masks[SEVERITY_MASK_IDX(LOG_WARN)] |= LD_BUG; add_stream_log(&s, "", fileno(stdout)); } { @@ -303,7 +303,7 @@ main(int c, const char **v) log_severity_list_t s; memset(&s, 0, sizeof(s)); set_log_severity_config(LOG_ERR, LOG_ERR, &s); - s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; + s.masks[SEVERITY_MASK_IDX(LOG_WARN)] |= LD_BUG; add_callback_log(&s, log_callback_failure); } flush_log_messages_from_startup(); diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c index 8ba6bf9fe4..727449e6eb 100644 --- a/src/test/testing_rsakeys.c +++ b/src/test/testing_rsakeys.c @@ -468,7 +468,7 @@ pk_generate_internal(int bits) *idxp += crypto_rand_int_range(1,3); *idxp %= n_pregen; return crypto_pk_dup_key(pregen_array[*idxp]); -#else /* !(defined(USE_PREGENERATED_RSA_KEYS)) */ +#else /* !defined(USE_PREGENERATED_RSA_KEYS) */ crypto_pk_t *result; int res; result = crypto_pk_new(); |