<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Frank DENIS random thoughts.</title>
  <link href="http://00f.net/atom.xml" rel="self"/>
  <link href="http://00f.net/"/>
  <updated>2012-05-11T08:53:03+02:00</updated>
  <id>http://00f.net</id>
  
  <author>
    <name>Frank Denis (Jedi/Sector One)</name>
  </author>
  
  
  <entry>
    <title>On the distribution of DNS TTLs</title>
    <link href="http://00f.net/2012/05/10/distribution-of-dns-ttls/"/>
   <updated>2012-05-10T00:00:00+02:00</updated>
   <id>http://00f.net/2012/05/10/distribution-of-dns-ttls</id>
   <content type="html">&lt;p&gt;I recently sampled 348,876,495 valid (actual records exist) A queries
processed by OpenDNS servers. This represents 6,263,672 unique names.&lt;/p&gt;

&lt;p&gt;And I then made a simple app that dumps the initial TTL (as reported by
authoritative servers) for each of these unique names, in order to check
what the TTL distribution looks like. This data was then processed with R.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/1.png&quot; alt=&quot;General overview&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Well, the TTL distribution looks &lt;em&gt;very&lt;/em&gt; unbalanced, to say the least.&lt;/p&gt;

&lt;p&gt;Do a lot of people publish records with a TTL that is longer than 30
days?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/2.png&quot; alt=&quot;More than 30 days&quot; /&gt;&lt;/p&gt;

&lt;p&gt;No, apparently, only a ridiculous amount of records happen to have a
TTL larger than 30 days.&lt;/p&gt;

&lt;p&gt;It's fun to see insane TTLs like 68 years (bbs.oouc.cn, beechglen.com,
canadianangling.com, capitoltalk.com, durhambannerexchange.com,
elimport.co.il, epictn.org, glutathioneforhealth.com, greeley.ca,
gregsushinsky.com, ...), although these are probably configuration
errors.&lt;/p&gt;

&lt;p&gt;Let's zoom into more reasonnable TTLs, that are 1 week or less.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/3.png&quot; alt=&quot;7 days or less&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Wow. Apparently, even TTLs longer than 1 day are very rare.
So let's shrink the window to TTLs that are no longer than 1 day.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/4.png&quot; alt=&quot;1 day or less&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A fair amount of records have been configured with a TTL that is
exactly 1 day, but the vast majority seems to be below 4 hours.&lt;/p&gt;

&lt;p&gt;Let's zoom in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/5.png&quot; alt=&quot;8 hours or less&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ok, at this point, it's probably reasonnable to keep zooming in:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/6.png&quot; alt=&quot;4 hours or less&quot; /&gt;&lt;/p&gt;

&lt;p&gt;or with 10 segments:
&lt;img src=&quot;/img/posts/ttl-distribution/7.png&quot; alt=&quot;4 hours or less - 10 segments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is still a very unbalanced distribution. 4 hours TTLs are common,
1 hour TTLs are more common, but the vast majority seems to be below
15 minutes.&lt;/p&gt;

&lt;p&gt;TTLs below 1 hour represent the hot spot, so let's zoom in:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/8.png&quot; alt=&quot;1 hours or less&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There's a fair amount of records with a 1 hour TTL, a high amount of
records with a TTL below or equal to 10 minutes, and pretty much
nothing in-between.&lt;/p&gt;

&lt;p&gt;Let's see what TTLs below 10 minutes look like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl-distribution/9.png&quot; alt=&quot;10 minutes or below&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, records within this interval are either below 5 minutes (with
peaks at 1 minute and 2 minutes), or 10 minutes.&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;table summary=&quot;TTL distribution&quot;&gt;
&lt;tr&gt;&lt;th&gt;TTL = 0&lt;/th&gt;&lt;td&gt;0.16 %&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;TTL &lt;= 1 minute&lt;/th&gt;&lt;td&gt;9.84 %&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;TTL &lt;= 2 minutes&lt;/th&gt;&lt;td&gt;16.32 %&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;TTL &lt;= 5 minutes&lt;/th&gt;&lt;td&gt;39.88 %&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;TTL &lt;= 1 hour&lt;/th&gt;&lt;td&gt;70.07 %&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;TTL &lt;= 1 day&lt;/th&gt;&lt;td&gt;98.89 %&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;Looks like there are still ways to make the internet faster.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Akamai vs public DNS servers</title>
    <link href="http://00f.net/2012/02/22/akamai-vs-public-dns-servers/"/>
   <updated>2012-02-22T00:00:00+01:00</updated>
   <id>http://00f.net/2012/02/22/akamai-vs-public-dns-servers</id>
   <content type="html">&lt;h1&gt;How a CDN works&lt;/h1&gt;

&lt;p&gt;&quot;Downloads from Apple are excruciatingly slow&quot;.
&quot;Don't use Google DNS or OpenDNS, or else iTunes will be slow&quot;
&quot;LOL, OpenDNS claims it makes the internet faster. Yeah, by telling
Akamai you are in the US and getting 200K/sec downloads in Oz…&quot;&lt;/p&gt;

&lt;p&gt;Fact: these claims are true.&lt;/p&gt;

&lt;p&gt;Apple uses Akamai, a popular CDN. The whole point of a CDN is to make
your downloads faster: CDNs have servers at multiple locations, and
when you request some content, CDNs are supposed to serve it from the
closest (and hopefully fastest) location.&lt;/p&gt;

&lt;p&gt;There are different ways to achieve this, but a very common one, that
Akamai also relies on, is to leverage the DNS system.&lt;/p&gt;

&lt;p&gt;When your client wants to resolve www.apple.com, Akamai DNS servers
are going to give different replies according to the source IP
address.&lt;/p&gt;

&lt;p&gt;But what source IP address? Yours? Not always. What Akamai consider is
actually the IP address of the DNS resolver hitting their servers.
If you're using a local resolver, that's cool. Akamai will know your
exact IP address, and hopefully redirect you to the closest server.&lt;/p&gt;

&lt;p&gt;If you're using your ISP's resolver, this is the IP Akamai will consider
in order to pick the server that will process your query. The result
may be the same as if you had used a local resolver. Or not. If the
DNS resolver is on a different subnet, Akamai can get confused and you
can be redirected to a server that actually is way off base.&lt;/p&gt;

&lt;p&gt;If you're using OpenDNS, Google DNS, Norton, Level 3, any other
public DNS service, or any other remote DNS resolver (for example,
through a VPN connection), Akamai will see the source IP address of
your remote resolver, too. Nor yours.&lt;/p&gt;

&lt;p&gt;And the server they will redirect you to would probably be an
excellent choice if you were Google or OpenDNS. But the very same
server can be a very poor choice for you.&lt;/p&gt;

&lt;p&gt;From the same origin, packets can take a totally different route in
order to reach Google, OpenDNS and Akamai. DNS queries sent to Google
or OpenDNS from Paris to Amsterdam can be super fast. But then,
downloads from Paris to an Akamai server in Amsterdam can be super
slow. Just because the routes can be totally different.&lt;/p&gt;

&lt;h1&gt;What about other CDNs?&lt;/h1&gt;

&lt;p&gt;How come this problem occurs with Akamai but not with other CDNs?&lt;/p&gt;

&lt;p&gt;Some other major CDNs don't have this problem because instead of
considering the resolver IP address, they actually pay attention to
the actual client network, in order to pick the closest/fastest
server.&lt;/p&gt;

&lt;p&gt;OpenDNS and Google DNS have been supporting the edns-client-subnet
extension for a long time. This mechanism was designed by Google
specifically to address this problem. And it works beautifully.
CDNs can send a redirection to the best server no matter what resolver
you use.&lt;/p&gt;

&lt;p&gt;Unfortunately, Akamai still don't support this mechanism.&lt;/p&gt;

&lt;h1&gt;Make an exception&lt;/h1&gt;

&lt;p&gt;It's totally possible to use Google, OpenDNS or any other public DNS
service and still avoid being redirected to a slow server by Akamai.&lt;/p&gt;

&lt;p&gt;Just make an exception.&lt;/p&gt;

&lt;p&gt;If you happen to run OSX, your operating system already provides a way
to use specific DNS resolvers in order to resolve specific domains.&lt;/p&gt;

&lt;p&gt;If you want queries to *.apple.com to go to your ISP's resolvers
instead of Google, OpenDNS, or any other default resolver, just create
a file named &lt;code&gt;/etc/resolver/apple.com&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;nameserver a.b.c.d
nameserver e.f.g.h
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Replace &lt;code&gt;a.b.c.d&lt;/code&gt; and &lt;code&gt;e.f.g.h&lt;/code&gt; with your ISP's resolvers. You can
have as many resolvers as you want, and as many exceptions as you
want. Just create files as needed. One per domain name.&lt;/p&gt;

&lt;p&gt;If you're running another operating system, you should be running a
local DNS cache. The Windows DNS cache is a total joke and most
Unix-like systems don't provide any cache at all.&lt;/p&gt;

&lt;p&gt;Running a cache like &lt;a href=&quot;http://unbound.net&quot;&gt;Unbound&lt;/a&gt; is the best way to
reduce latency due to DNS queries, even if queries are forwarded to
Google or OpenDNS. In addition to caching queries, modern resolvers
have nice features like the ability to automatically prefetch records
before they expire. For CDNs and social networks using stupidily low
TTLs, it can really make a difference.&lt;/p&gt;

&lt;p&gt;Once you are running a cache, and it has been configured to forward
queries to OpenDNS, Google, or anything else, you can configure
exceptions.&lt;/p&gt;

&lt;p&gt;With Unbound, it's as simple as adding two lines to the configuration
file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;forward-zone:
    name: &quot;apple.com&quot;
    forward-addr: a.b.c.d
    forward-addr: e.f.g.h
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The edns-client-subnet mechanism has been around for quite some time
now, so all modern CDNs are eventually going to support it, and
manually adding exceptions won't be required any more.&lt;/p&gt;

&lt;p&gt;But as we speak, this is still required, at least for Akamai.&lt;/p&gt;

&lt;p&gt;Fortunately, adding exceptions is easy. So you can totally work around
CDNs that don't support a DNS mechanism that was designed for them,
and still enjoy the benefits of fast and reliable DNS services in
order to access content served by other services.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>DNS records and TTL - how long does a second actually last?</title>
    <link href="http://00f.net/2011/11/17/how-long-does-a-dns-ttl-last/"/>
   <updated>2011-11-17T00:00:00+01:00</updated>
   <id>http://00f.net/2011/11/17/how-long-does-a-dns-ttl-last</id>
   <content type="html">&lt;p&gt;In a DNS zone, every record carries its own time-to-live, so that it can
be cached, yet still changed if necessary.&lt;/p&gt;

&lt;p&gt;This information is originally served by authoritative servers for the
related zone. The TTL is represented as an integer number of seconds.&lt;/p&gt;

&lt;p&gt;At first sight, the mechanism looks straightforward: if the
&lt;code&gt;www.example.com&lt;/code&gt; record has a TTL of 30, it's only valid up to 30
second, and caches must fetch it again if requested after this delay.&lt;/p&gt;

&lt;h1&gt;Chained caches&lt;/h1&gt;

&lt;p&gt;DNS caches can be chained: instead of directly querying authoritative
servers, a cache can forward queries to a another server, and cache
the result.&lt;/p&gt;

&lt;p&gt;Having 3 or 4 chained caches is actually very common. Web browsers,
operating systems and routers can cache and forward DNS a query.
Eventually, this query will be sent to an upstream cache, like the ISP
cache or a third-party service like &lt;em&gt;OpenDNS&lt;/em&gt;. And these caches can also
actually hide multiple chained caches.&lt;/p&gt;

&lt;p&gt;In order to respect the original TTL, caches are modifying records as
they forward them to clients. The response to a query that has been
sitting in a cache for 10 second will be served with a TTL reduced by
10 second. That way, if the original TTL was 30 second, the whole
chain is guaranteed to consider this record as expired as the same
time: the original meaning of the TTL is retained no matter how many
resolvers there are in the way.&lt;/p&gt;

&lt;p&gt;Well, not exactly. A TTL is just a time interval, not an absolute
date. Unlike a HTTP response, a DNS response doesn't contain any
timestamp. Thus, requests processing and network latency are causing
caches to keep a record longer than they actually should in order to
respect the initial TTL.&lt;/p&gt;

&lt;p&gt;A TTL being an integer value makes things even worse: a chain of &lt;strong&gt;N&lt;/strong&gt; caches
can introduce a &lt;strong&gt;N&lt;/strong&gt; second bias.&lt;/p&gt;

&lt;p&gt;In practice, this is rarely an issue: TTLs as served by authoritative
servers are considered indicative, and not as something to depend on
when accurate timing is required.&lt;/p&gt;

&lt;h1&gt;The decay of a TTL, as seen by different pieces of software&lt;/h1&gt;

&lt;p&gt;How does the TTL of a record served by a DNS cache decays over time?&lt;/p&gt;

&lt;p&gt;Surprisingly, different implementations exhibit different behaviors.&lt;/p&gt;

&lt;p&gt;For a record initially served with a TTL equal to &lt;strong&gt;N&lt;/strong&gt; by authoritative
servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Google DNS&lt;/em&gt; serves it with a TTL in the interval &lt;strong&gt;[0, N-1]&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;dnscache&lt;/em&gt; is serving it with a TTL in the interval &lt;strong&gt;[0, N]&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Unbound&lt;/em&gt; serves it with a TTL in the interval &lt;strong&gt;[0, N]&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Bind&lt;/em&gt; serves it with a TTL in the interval &lt;strong&gt;[1, N]&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;PowerDNS Recursor&lt;/em&gt; always serves it with a TTL of &lt;strong&gt;N&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Hold on... Does it mean that how frequently an entry will actually be
refreshed depends on what software resolvers are running?&lt;/p&gt;

&lt;p&gt;Sadly, yes. Given a record with a TTL equal to &lt;strong&gt;N&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Bind&lt;/em&gt; and &lt;em&gt;PowerDNS Recursor&lt;/em&gt; refresh it every &lt;strong&gt;N&lt;/strong&gt; second if necessary&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Unbound&lt;/em&gt; and &lt;em&gt;dnscache&lt;/em&gt; are only refreshing it every &lt;strong&gt;N + 1&lt;/strong&gt; seconds at best.&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;TTL Zero&lt;/h1&gt;

&lt;p&gt;Although &lt;a href=&quot;http://mark.lindsey.name/2009/03/never-use-dns-ttl-of-zero-0.html&quot;&gt;a TTL of zero can cause interoperability issues&lt;/a&gt;,
most DNS caches are considering records with a TTL of zero as records
that should not be cached.&lt;/p&gt;

&lt;p&gt;This perfectly makes sense when the TTL of zero is the original TTL, as served
by authoritative servers.&lt;/p&gt;

&lt;p&gt;However, when a cache artificially changes the TTL to zero, it changes a record
that had been designed for being cached to an uncachable record that
contaminates the rest of the chain.&lt;/p&gt;

&lt;h1&gt;dnscache and a 1 second record&lt;/h1&gt;

&lt;p&gt;To illustrate this, a Linux box has been setup with a local DNS cache.
&lt;em&gt;Unbound&lt;/em&gt; has been chosen, but the last component of the chain actually
makes little difference. Even web browsers caches have a very similar
behavior.&lt;/p&gt;

&lt;p&gt;Queries are forwarded to an upstream cache on the same LAN, running
&lt;em&gt;dnscache&lt;/em&gt;, and outgoing queries are recorded with ngrep. The same
query, whose response has a TTL of &lt;strong&gt;1&lt;/strong&gt;, is made at a 10 queries per
second rate.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;X&lt;/strong&gt; axis represents outgoing queries, whereas the &lt;strong&gt;Y&lt;/strong&gt; axis is the time
elapsed since the previous query.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl/unbound-djbdns-one.jpg&quot; alt=&quot;One sec TTL with an upstream server running dnscache&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Even though a TTL of &lt;strong&gt;1&lt;/strong&gt; is served by authoritative servers for this
record, a large amount of responses are cached and served for &lt;strong&gt;2&lt;/strong&gt;
second. This is due to the local &lt;em&gt;Unbound&lt;/em&gt; resolver.&lt;/p&gt;

&lt;p&gt;However, there is also quite a lot of responses that haven't been cached at
all. It happens when &lt;em&gt;dnscache&lt;/em&gt; serves a response with a TTL of &lt;strong&gt;0&lt;/strong&gt;. Since
it happens one third of the time, this is suboptimal and not on par
with the intent of the authoritate record, which is served with
non-zero TTL.&lt;/p&gt;

&lt;p&gt;And although &lt;em&gt;dnscache&lt;/em&gt; and &lt;em&gt;Unbound&lt;/em&gt; are handling TTLs the same way, we
can't expect their caches to be perfectly synchronized. When the local
cache considers a record as expired and issues an outgoing query, the
upstream server can consider it as not expired yet, just in the middle
of the last second. What we get is a constant race between caches,
causing jitter and outgoing queries sent after 1 second.&lt;/p&gt;

&lt;h1&gt;How different implementations can affect the number of outgoing queries&lt;/h1&gt;

&lt;p&gt;The following experiment has been made by observing outgoing queries
when sending &lt;strong&gt;10,000 queries&lt;/strong&gt; for a record with a TTL of &lt;strong&gt;2&lt;/strong&gt;, at an average
10 qps rate, to a local cache, using &lt;em&gt;Google&lt;/em&gt;, &lt;em&gt;OpenDNS&lt;/em&gt; and
&lt;em&gt;Level 3&lt;/em&gt; as upstream resolvers.&lt;/p&gt;

&lt;p&gt;Here is the number of outgoing queries that were required to complete
the &lt;strong&gt;10,000 local queries&lt;/strong&gt; using different services:&lt;/p&gt;

&lt;table&gt;
  &lt;tr scope=column&gt;
    &lt;th&gt;Service&lt;/th&gt;
    &lt;th&gt;Outgoing queries&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Google DNS&lt;/td&gt;
    &lt;td&gt;1383&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;OpenDNS&lt;/td&gt;
    &lt;td&gt;632&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Level 3&lt;/td&gt;
    &lt;td&gt;388&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;


&lt;p&gt;&lt;img src=&quot;/img/posts/ttl/unbound-remote.jpg&quot; alt=&quot;Same queries, same local resolver, but using different upstream caches: Google DNS, _OpenDNS_ and Level 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When using &lt;em&gt;Google DNS&lt;/em&gt; (blue dots), queries are effectively never cached
more than &lt;strong&gt;2 second&lt;/strong&gt;, even locally. This is due to the max TTL returned
by &lt;em&gt;Google DNS&lt;/em&gt; being the initial TTL &lt;strong&gt;minus one&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;em&gt;Google DNS&lt;/em&gt; returns a TTL of &lt;strong&gt;zero&lt;/strong&gt;, we observe the same jitter and
the same slew of queries that couldn't be served from the local cache.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;OpenDNS&lt;/em&gt; (orange dots) has the same behavior as &lt;em&gt;dnscache&lt;/em&gt; and &lt;em&gt;Unbound&lt;/em&gt;.,
with TTLs in the &lt;strong&gt;[0, N]&lt;/strong&gt; interval.
Our initial TTL with a value of &lt;strong&gt;2&lt;/strong&gt; actually causes &lt;strong&gt;3, 2, and 1 second
delays&lt;/strong&gt; between request, plus a sensitive amount of consecutive
outgoing queries due a null TTL.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Level 3&lt;/em&gt; (green dots) is running &lt;em&gt;Bind&lt;/em&gt;, which returns a TTL in the &lt;strong&gt;[1, N]&lt;/strong&gt;
interval. &lt;em&gt;Bind&lt;/em&gt; caches records &lt;strong&gt;one second less&lt;/strong&gt; than &lt;em&gt;dnscache&lt;/em&gt; and &lt;em&gt;Unbound&lt;/em&gt;.
Because our local resolver is &lt;em&gt;Unbound&lt;/em&gt;, queries received with
a TTL of &lt;strong&gt;N&lt;/strong&gt; are actually cached &lt;strong&gt;N + 1&lt;/strong&gt; second.
But as expected, the frequency of required outgoing queries very rarely
exceeds &lt;strong&gt;3 second&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What the correct behavior is, is out of scope of this article. All
major implementations are probably correct.&lt;/p&gt;

&lt;p&gt;But from a user perspective, with only &lt;strong&gt;2&lt;/strong&gt; caches in the chain, and for
a given record, the same set of queries can require &lt;strong&gt;up to 3.5 more
outgoing queries&lt;/strong&gt; to get resolved, depending on what software the
remote cache is running.&lt;/p&gt;

&lt;p&gt;With CDNs and popular web sites having records with a very low TTL
(Facebook has a 30 second TTL, Skyrock has a 10 second TTL), the way a
cache handles TTLs can have a sensible impact on performance. That
said, some resolvers can be configured to pre-fetch records before
they expire, effectively mitigating this problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; &lt;em&gt;OpenDNS&lt;/em&gt; resolvers now behave like Bind: TTLs are now
in the &lt;strong&gt;[1, N]&lt;/strong&gt; interval.&lt;/p&gt;

&lt;h1&gt;The OSX cache&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;MacOS X&lt;/em&gt; provides a system-wide name cache, which is enabled by default.&lt;/p&gt;

&lt;p&gt;Its behavior is quite surprising, though. It seems to enforce a
&lt;strong&gt;minimum TTL of 12.5 seconds&lt;/strong&gt;, while still requiring some outgoing
queries delayed by the initial TTL value, and some consecutive ones.
Resolving the &lt;strong&gt;10,000 queries&lt;/strong&gt; from the previous test took only an average
of &lt;strong&gt;232 outgoing queries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Using &lt;em&gt;Google DNS&lt;/em&gt;, &lt;em&gt;OpenDNS&lt;/em&gt; and &lt;em&gt;Level 3&lt;/em&gt; as a remote resolver
produce the same result, with the exception on &lt;em&gt;Level 3&lt;/em&gt; (&lt;em&gt;Bind&lt;/em&gt;)
avoiding frequent (less than 1 sec) consecutive queries during the
time other return a TTL of zero.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/posts/ttl/osx.jpg&quot; alt=&quot;OSX built-in resolver&quot; /&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Programmatically changing network configuration on OSX</title>
    <link href="http://00f.net/2011/08/14/programmatically-changing-network-configuration-on-osx/"/>
   <updated>2011-08-14T00:00:00+02:00</updated>
   <id>http://00f.net/2011/08/14/programmatically-changing-network-configuration-on-osx</id>
   <content type="html">&lt;p&gt;Programmatically changing DNS resolvers, IP addresses and proxies on
OSX isn't rocket science but finding documentation on that is like
looking for a needle in a haystack.&lt;/p&gt;

&lt;p&gt;Changing the DNS configuration could have been as simple as updating
&lt;code&gt;/etc/resolv.conf&lt;/code&gt;, as OSX does provide an &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file. But
unlike every other Unix variant out there, OSX doesn't actually read
this file, as it is only there for compatibility with some legacy
tools.&lt;/p&gt;

&lt;h1&gt;Meet configd&lt;/h1&gt;

&lt;blockquote&gt;&lt;p&gt;&quot;The configd daemon is responsible for many configuration aspects of the
local system. configd maintains data reflecting the desired and current
state of the system, provides notifications to applications when this
data changes, and hosts a number of configuration agents in the form of
loadable bundles.&quot;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Think about it as a centralized key/value registry. Applications can
watch for changes, add new key/value pairs and modify existing
entries.&lt;/p&gt;

&lt;p&gt;The scutil(8) command provides a command-line interface to this registry:&lt;/p&gt;

&lt;pre&gt;
$ scutil
&amp;gt; list
  subKey [0] = Plugin:IPConfiguration
  subKey [1] = Plugin:InterfaceNamer
  subKey [2] = Setup:
  subKey [3] = Setup:/
  subKey [4] = Setup:/Network/BackToMyMac
  subKey [5] = Setup:/Network/Global/IPv4
  subKey [6] = Setup:/Network/HostNames
  subKey [7] = Setup:/Network/Interface/en0/AirPort
  subKey [8] = Setup:/Network/Service/0CE7A64C-9174-41AC-B634-41F4B6B5FE02
  subKey [9] = Setup:/Network/Service/0CE7A64C-9174-41AC-B634-41F4B6B5FE02/IPv4
  subKey [10] = Setup:/Network/Service/0CE7A64C-9174-41AC-B634-41F4B6B5FE02/IPv6
  subKey [11] = Setup:/Network/Service/0CE7A64C-9174-41AC-B634-41F4B6B5FE02/Interface
  subKey [12] = Setup:/Network/Service/0CE7A64C-9174-41AC-B634-41F4B6B5FE02/Proxies
...  
  subKey [32] = Setup:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E
  subKey [33] = Setup:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/IPv4
  subKey [34] = Setup:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/IPv6
  subKey [35] = Setup:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/Interface
  subKey [36] = Setup:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/Proxies
...
  subKey [58] = Setup:/System
  subKey [59] = State:/IOKit/LowBatteryWarning
  subKey [60] = State:/IOKit/Power/CPUPower
  subKey [61] = State:/IOKit/PowerAdapter
  subKey [62] = State:/IOKit/PowerManagement/Assertions
  subKey [63] = State:/IOKit/PowerManagement/CurrentSettings
  subKey [64] = State:/IOKit/PowerManagement/SystemLoad
  subKey [65] = State:/IOKit/PowerManagement/SystemLoad/Detailed
  subKey [66] = State:/IOKit/PowerSources/InternalBattery-0
  subKey [67] = State:/IOKit/SystemPowerCapabilities
  subKey [68] = State:/Network/BackToMyMac
  subKey [69] = State:/Network/Connectivity
  subKey [70] = State:/Network/Global/DNS
  subKey [71] = State:/Network/Global/IPv4
  subKey [72] = State:/Network/Global/Proxies
  subKey [73] = State:/Network/Interface
  subKey [74] = State:/Network/Interface/en0/AirPort
  subKey [75] = State:/Network/Interface/en0/CaptiveNetwork
  subKey [76] = State:/Network/Interface/en0/IPv4
  subKey [77] = State:/Network/Interface/en0/IPv6
  subKey [78] = State:/Network/Interface/en0/Link
  subKey [79] = State:/Network/Interface/lo0/IPv4
  subKey [80] = State:/Network/Interface/lo0/IPv6
  subKey [81] = State:/Network/Interface/p2p0/Link
  subKey [82] = State:/Network/Interface/utun0/IPv6
  subKey [83] = State:/Network/MulticastDNS
  subKey [84] = State:/Network/NetBIOS
  subKey [85] = State:/Network/PrivateDNS
  subKey [86] = State:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/DHCP
  subKey [87] = State:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/DNS
  subKey [88] = State:/Network/Service/527FC752-8B1D-4111-A205-4E81F757704E/IPv4
  subKey [89] = State:/Users/ConsoleUser
  subKey [90] = com.apple.DirectoryService.NotifyTypeStandard:DirectoryNodeAdded
  subKey [91] = com.apple.network.identification
  subKey [92] = com.apple.opendirectoryd.node:/Search
  
&amp;gt; get State:/Network/Global/DNS

&amp;gt; d.show
&amp;lt;dictionary&amp;gt; {
  ServerAddresses : &amp;lt;array&amp;gt; {
    0 : 208.67.220.220
    1 : 208.67.222.222
  }
}
&lt;/pre&gt;


&lt;p&gt;As we can see, the registry holds most of the network settings,
including DNS settings. The &lt;code&gt;Setup:/*&lt;/code&gt; entries contain every available
network configuration (as configured in the preferences pane), whereas
&lt;code&gt;State:/*&lt;/code&gt; entries include only what's currently active.&lt;/p&gt;

&lt;p&gt;There are global DNS settings and configuration-specific DNS setings.&lt;/p&gt;

&lt;h1&gt;Programmatically accessing the registry&lt;/h1&gt;

&lt;p&gt;The overlooked &lt;code&gt;DynamicStore&lt;/code&gt; API, which is part of the SystemConfiguration
framework, let us access the &lt;code&gt;configd&lt;/code&gt; registry.&lt;/p&gt;

&lt;p&gt;The following code snippet opens the registry, looks for active
DNS-related entries, and updates them with a new property list
containing OpenDNS resolvers.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;c&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;SystemConfiguration/SystemConfiguration.h&amp;gt;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setDNS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CFStringRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFIndex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCDynamicStoreRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCDynamicStoreCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;setDNS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;CFArrayRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFArrayCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resolvers_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kCFTypeArrayCallBacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;CFDictionaryRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFDictionaryCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CFStringRef&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;ServerAddresses&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kCFTypeDictionaryKeyCallBacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kCFTypeDictionaryValueCallBacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;    
    
    &lt;span class=&quot;n&quot;&gt;CFArrayRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCDynamicStoreCopyKeyList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;State:/Network/(Service/.+|Global)/DNS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    
    &lt;span class=&quot;n&quot;&gt;CFIndex&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFArrayGetCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCDynamicStoreSetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFArrayGetValueAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CFStringRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;208.67.220.220&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;208.67.222.222&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;setDNS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CFIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]));&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Of course, changing the registry requires root privileges, but that's
all it takes.&lt;/p&gt;

&lt;p&gt;As new entries can be seamlessly created, you can use the registry for
centralizing configuration in your own apps, or just to save previous
entries before altering them.&lt;/p&gt;

&lt;h1&gt;Changing network services&lt;/h1&gt;

&lt;p&gt;The technique describe above works fine. But the new settings aren't
persistent. More often than once, this actually comes in handy, in
order to apply temporary settings and to rest assured that once the
user reboots his machine, network settings will always be back to a
sane state.&lt;/p&gt;

&lt;p&gt;Tweaking the system configuration database is not recomended by Apple
because some daemons may not be aware of a configuration change.
In addition, you may want to permanently apply the new settings.&lt;/p&gt;

&lt;p&gt;Another way to tackle this problem is to actually alter the network
services.&lt;/p&gt;

&lt;p&gt;Under OSX, a network service is a configuration for a network interface.
To apply a change globally, we need to step over all network services,
check whether DNS (or whatever we want to change) is a support
protocol, and make the related changes.&lt;/p&gt;

&lt;p&gt;This achieves the same thing as what one can do from the Network
preferences pane.&lt;/p&gt;

&lt;p&gt;Almost.&lt;/p&gt;

&lt;p&gt;After changing settings this way, you will notice that ths Network
preferences pane properly reflects them. However, some apps won't have
caught up with the changes. For example, the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; isn't
automatically updated.&lt;/p&gt;

&lt;p&gt;The trick here is to open the system configuration registry as
described above, and then to close it without having done any actual
change. Just &quot;touching&quot; the registry will trigger observers like
&lt;code&gt;configd&lt;/code&gt; so that they can do their own magic.&lt;/p&gt;

&lt;p&gt;An empty DNS servers list or an empty IP/network address means that
DHCP will be used in order to retrieve this information.
But this doesn't happen automatically as the new settings are applied.
Explicitly renewing the DHCP lease is required, by calling
&lt;code&gt;SCNetworkInterfaceForceConfigurationRefresh()&lt;/code&gt; on each interface.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;objc&quot;&gt;&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DNSGlobalSettings&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;DNSSupportForNetworkService:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SCNetworkServiceRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SCNetworkServiceGetEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCNetworkInterfaceRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkServiceGetInterface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supportedProtocols&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge_transfer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkInterfaceGetSupportedProtocolTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;supportedProtocols&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;indexOfObject:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kSCNetworkProtocolTypeDNS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSNotFound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;setResolversTo:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;forNetworkService:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SCNetworkServiceRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;DNSSupportForNetworkService:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkServiceCopyProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kSCNetworkProtocolTypeDNS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolGetEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;disabled&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;isKindOfClass:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;Setting this service&amp;#39;s resolvers to DHCP&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;Setting this service&amp;#39;s resolvers to [%@]&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DNSDict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolGetConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSMutableDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newDNSDict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DNSDict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;This interface had an existing configuration&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkServiceCopyProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kSCNetworkProtocolTypeDNS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolGetEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;Removing protocol from configuration&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolSetConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newDNSDict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSMutableDictionary&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;dictionaryWithDictionary:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DNSDict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newDNSDict&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;removeObjectForKey:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kSCPropNetDNSServerAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newDNSDict&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;setValue:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;forKey:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kSCPropNetDNSServerAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;This interface had no existing configuration&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;            
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;newDNSDict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSMutableDictionary&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;dictionaryWithObject:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;forKey:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kSCPropNetDNSServerAddresses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkProtocolSetConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkProtocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFDictionaryRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newDNSDict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchDynamicStore&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCDynamicStoreRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCDynamicStoreCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;myapp&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CFRelease&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;DynamicStore updated&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;forceDHCPUpdate&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@&amp;quot;Forcing DHCP update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interfaces&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge_transfer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkInterfaceCopyAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interface_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interfaces&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SCNetworkInterfaceRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkInterfaceRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interface_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SCNetworkInterfaceForceConfigurationRefresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;applyToNetworkServices:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SCNetworkServiceRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;andCommit:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCPreferencesRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCPreferencesCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CFSTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;myapp&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCPreferencesLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCNetworkSetRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkSet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkSetCopyCurrent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkSetServices&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge_transfer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkSetCopyServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkSetServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SCNetworkServiceRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__bridge&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SCNetworkServiceRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SCPreferencesUnlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SCPreferencesCommitChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;SCPreferencesApplyChanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;CFRelease&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;networkSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;    
    &lt;span class=&quot;n&quot;&gt;CFRelease&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;touchDynamicStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;setResolvers:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSArray&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;applyToNetworkServices:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SCNetworkServiceRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;setResolversTo:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;forNetworkService:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;networkService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;andCommit:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;   
    &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSNotificationCenter&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;postNotificationName:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&amp;quot;CONFIGURATION_CHANGED&amp;quot;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;object:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;userInfo:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
  </entry>
  
  <entry>
    <title>Redis as a recommendation engine</title>
    <link href="http://00f.net/2011/03/10/redis-as-a-recommendation-engine/"/>
   <updated>2011-03-10T00:00:00+01:00</updated>
   <id>http://00f.net/2011/03/10/redis-as-a-recommendation-engine</id>
   <content type="html">&lt;p&gt;&lt;em&gt;&quot;You might be interested in red toilet paper, because you bought blue
and green toilet paper, and people who also bought blue and green
toilet paper tend to also buy red toilet paper&quot;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recommendation engines are now everywhere, from e-commerce to social networks.&lt;/p&gt;

&lt;p&gt;Graphs databases like Neo4j are probably the best way to tackle the
problem. But as an alternative, let's try building a recommendation
engine on Redis.&lt;/p&gt;

&lt;p&gt;Who bought what or who is following who could be stored in Redis
sorted sets.&lt;/p&gt;

&lt;p&gt;Let's take an example: a user buys A, and also B. We increment the
score of the B item in the sorted set associated to key A. And we
possibly also do it the other way round.&lt;/p&gt;

&lt;p&gt;This is a trivial way to keep a list of what items have been bought,
what are the related items and how many of them there are.&lt;/p&gt;

&lt;p&gt;On a social network, the score associated to a relationship may reflect the
level of trust.&lt;/p&gt;

&lt;p&gt;Let's suppose we have the following relationships:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;B -&amp;gt; C
D -&amp;gt; C
E -&amp;gt; F
A -&amp;gt; B
A -&amp;gt; D
A -&amp;gt; E
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;C is referenced by B and D. B and D are referenced by A.
But C isn't referenced by A yet. This is presumably something to suggest.&lt;/p&gt;

&lt;p&gt;F might also be a good candidate as a suggestion for A. However, since
there's only a single path from A to F (through E), it's probably less
relevant than C.&lt;/p&gt;

&lt;p&gt;In order to limit the number of non-relevant results, we may want to
add a threshold: nodes that have less possible paths than a cutoff
value shouldn't be suggested.&lt;/p&gt;

&lt;p&gt;How to do that with Redis, and only Redis?&lt;/p&gt;

&lt;p&gt;Here's one possible way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use ZUNIONSTORE in order to build a temporary aggregate of all
related items (scores are summed up), and remove the reference item
from the set (cylic links).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remove everything from the aggregate that is already directly
related to the reference item.
One way to achieve this on a sorted set is to use ZUNIONSTORE
AGGREGATE MIN with a negative or null weight for the set we want to
delete, followed by ZREMRANGEBYSCORE in order to actually remove
everything from the reference set and everything below the cutoff
score.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sort and optionally trim the new set with ZREVRANGE et voila!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Maybe code speaks louder than words, so here we go for some code:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;redis&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Recommendation&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:db&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@db&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;  
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;suggestions_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suggestions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Item&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;recommendation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recommendation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;{rec}:items:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tmp_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;{rec}:_tmp:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;linked_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zincrby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linked_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;suggestions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;cutoff&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cutoff&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:limit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;related_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;related_keys&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;multi&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zunionstore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;related_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zrem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zunionstore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:weights&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                          &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;del&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zremrangebyscore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;-inf&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zrevrange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;del&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;    
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Example&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Recommendation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;f&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_also_bought&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;e&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suggestions_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
  </entry>
  
</feed>


