=== NAME ===

mararc - Format of the mararc zone file that MaraDNS uses 

=== MARARC FILE FORMAT ===

Mararc files use a syntax that is a subset of Python 2.2.3 syntax. In 
particular, Python 2.2.3 (and possibly other versions of Python) can 
read a properly formatted mararc file without error. 

Unlike Python, however, a mararc file can only use certain variable 
names, and the variables can only be declared as described below.

=== COMMENTS ===

Comments (lines ignored by the MaraDNS parser) start with the '#' 
character, like this:

# This is a comment

The MaraDNS parser also ignores lines which contain only white 
space. 

=== OPERATORS ===

The MaraRC file supports two operators: = and += 

The = operator can be used to assign both numeric and string values 

The += operator can only be used on string values, and concatenates the 
value to the right of the += operator to the string specified to the 
left of the += operator. 

Examples:

ipv4_bind_addresses = "10.2.19.83" 
ipv4_bind_addresses += ",10.2.66.74" 
ipv4_bind_addresses += ",10.3.87.13"

ipv4_bind_addresses now has the value 
"10.2.19.83,10.2.66.74,10.3.87.13"

ipv4_alias["icann"] = "198.41.0.4" 
ipv4_alias["icann"] += ",192.228.79.201" 
ipv4_alias["icann"] += ",192.33.4.12,128.8.10.90"

=== MARARC VARIABLES ===

Follows is a listing of variables that can be declared in the mararc 
file. 

=== DICTIONARY VARIABLE FORMAT ===

A dictionary variable is an array that can have multiple elements. 
Unlike a traditional array, these arrays are indexed by strings instead 
of numbers. These are analogous to associative arrays, or what Perl 
somewhat inaccurately calls hashes. 

The syntax of a dictionary variable is in the following form:

name["index"] = "value"

Where name is the name of the dictionary variable, index is the 
index of the array, and value is the value stored at that index. 

Every time we have a dictionary-type variable (such as csv2), we must 
first initialize it using a line in the following form:

csv2 = {}

Here, csv2 is the name of the "dictionary" variable that we are 
initializing. 

=== DICTIONARY VARIABLES ===

Here is a listing of all "dictionary"-style variables that MaraDNS 
uses: 

== csv2 ==

The csv2 dictionary variable stores all of the zone names and file 
names for the zone files that MaraDNS uses. Note that csv2 files are 
read after MaraDNS is chrooted. Hence the filename is relative to the 
chroot_dir. Example:

csv2["example.net."] = "db.example.net"

See csv2(5) for a description of this file's format. 

The dictionary index (zone name) can not have a * in it. If it does, 
MaraDNS will terminate with an "Illegal zone name" error. 

Please note that, in order to reload a zone file, it is necessary to 
restart MaraDNS and reload all zone files. MaraDNS uses a hash data 
format which loads records very quickly from memory, but requires a 
restart to update. 

== csv1 ==

csv1: Used to indicate the filename to use for a given zone stored in 
the legacy csv1 zone file format. This is primarily for compatibility 
with people who have maradns-1.0 zone files.

csv1["zone"] = "filename"

csv1: A pipe-separated-file. See csv1(5). 

zone: the zone that file in question is authoritative for 

filename: the file with the CSV1 zone data 

Note that csv1 files are read after MaraDNS is chrooted, and, hence the 
filename is relative to the chroot_dir. 

See the csv1(5) man page for more information on this file format. 

== ipv4_alias ==

ipv4_alias: Used to give nicknames or aliases for ip/netmask pairs for 
ipv4 (standard 32-bit) IP addresses.

ipv4_alias["name"] = "ip1/netmask,ip2/netmask,etc"

name: The name of the alias in question 

ip: The ip portion of an ip/netmask pair 

netmask: the mask portion of an ip/netmask pair 

,: Used to separate ip/netmask pairs. Spaces may be placed before or 
after this comma. 

An ip is in dotted-decimal format, e.g. "10.1.2.3". 

The netmask can be in one of two formats: A single number between 1 and 
32, which indicates the number of leading "1" bits in the netmask, or a 
4-digit dotted-decimal netmask. 

The netmask is used to specify a range of IPs.

== ipv4_alias examples ==

10.1.1.1/24 indicates that any ip from 10.1.1.0 to 10.1.1.255 will 
match. 

10.1.1.1/255.255.255.0 is identical to 10.1.1.1/24 

10.2.3.4/16 indicates that any ip from 10.2.0.0 to 10.2.255.255 will 
match. 

10.2.3.4/255.255.0.0 is identical to 10.2.3.4/16 

127.0.0.0/8 indicates that any ip with "127" as the first octet 
(number) will match. 

127.0.0.0/255.0.0.0 is identical to 127.0.0.0/8 

The netmask is optional, and, if not present, indicates that only a 
single IP will "match". e.g: 

10.9.9.9/32, 10.9.9.9/255.255.255.255, and 10.9.9.9 are all 
functionally identical, and indicate that only the ip 10.9.9.9 will 
match. 

The significance of "match" depends on what we use the ipv4 alias for. 

ipv4 aliases can nest. E.g:

ipv4_alias["susan"] = "10.6.7.8/24"  
ipv4_alias["office"] = "susan,10.9.9.9"

Where "susan" in the "office" alias matches the value of the 
ipv4_alias susan. 

Multiple levels of nesting are allowed. Self-referring nests will 
result in an error.

=== NORMAL VARIABLE FORMAT ===

Normal variables. These are variables that can only take a single 
value. 

The syntax of a normal variable is in the form

name = "value"

Where name is the name of the normal variable, and value is the 
value of the variable in question. 

=== NORMAL VARIABLES ===

Here is a listing of normal variables that MaraDNS uses: 

== ipv4_bind_addresses ==

ipv4_bind_addresses: The IP addresses to give the MaraDNS server. 

This accepts one or more ipv4 IPs in dotted-decimal (e.g. "127.0.0.1") 
notation, and specifies what IP addresses the MaraDNS server will 
listen on. Multiple bind addresses are separated with a comma, like 
this: "10.1.2.3, 10.1.2.4, 127.0.0.1"

== admin_acl ==

This is a list of ip/netmask pairs that are allowed to get certain 
administrative information about MaraDNS, including: 

* The version number of MaraDNS running

* The number of threads MaraDNS has

* MaraDNS' internal timestamp value

Note that this information is not available unless the mararc variable 
debug_msg_level is sufficiently high. See the information on 
debug_msg_level below for details on this and on the TXT queries sent 
to get the above information. 

== bind_address ==

bind_address: The IP address to give the MaraDNS server. 

This accepts a single IP in dotted-decimal (e.g. "127.0.0.1") notation, 
and specifies what IP address the MaraDNS server will listen on. Note 
that ipv4_bind_addresses has the same functionality. This name is 
included so that old MaraDNS configuration files will continue to work 
with new MaraDNS releases.

== bind_star_handling ==

In the case where there is both a star record for a given name and 
recordtype, a non-star record with the same name but a different 
recordtype, and no record for the given name and recordtype, MaraDNS 
will usually return the star record. BIND, on the other hand, will 
return a "not there" reply. In other words: 

* If a non-A record for foo.example.com exists

* An A record for *.example.com exists

* No A record for foo.example.com exists

* And the user asks for the A record for foo.example.com

* MaraDNS will usually return the A record attached to *.example.com

* BIND, on the other hand, returns a "not there" for foo.example.com

If the BIND behavior is desired, set bind_star_handling to 1. 
Otherwise, set this to 0. In MaraDNS 1.3, this has a default value of 
1. 

In addition, if there is a star record that could match any given 
record type, when bind_star_handling is 1, it makes sure that MaraDNS 
does not incorrectly return a NXDOMAIN (RFC 4074 section 4.2). 

Also, if bind_star_handling has a value of 2, MaraDNS will handle the 
following case exactly as per section 4.3.3 of RFC1034: 

* If a record for foo.example.com exists

* An A record for *.example.com exists

* And the user asks for the A record for bar.foo.example.com

* MaraDNS will usually return the A record attached to *.example.com

* RFC1034 section 4.3.3 says one should return a NXDOMAIN.

MaraDNS will exit with a fatal error if bind_star_handling has any 
value besides 0, 1, or 2. 

== chroot_dir ==

chroot_dir: The directory MaraDNS chroots to 

This accepts a single value: The full path to the directory to use as a 
chroot jail. 

Note that csv1 zone files are read after the chroot operation. Hence, 
the chroot jail needs to have any and all zone files that MaraDNS will 
load. 

== csv2_default_zonefile ==

This is a special zone file that allows there to be stars at the end of 
hostnames. This file is similar to a normal csv2 zone file, but has the 
following features and limitations: 

* Stars are allowed at the end of hostnames

* A SOA record is mandatory

* NS records are mandatory

* Neither CNAME, FQDN4, nor FQDN6 records are permitted in the zone 
  file

* Delegation NS records are not permitted in the zone file

* Default zonefiles may not be transferred via zone transfer

* Both recursion and default zonefiles may not be enabled at the same 
  time

== csv2_synthip_list ==

Sometimes the IP list of nameservers will be different than the 
nameservers one is bound to. This allows the synthetic nameserver list 
to have different IPs. 

Note that this may act in an unexpected manner if routable and 
non-routable (localhost and RFC1918) addresses are combined; in 
particular, a list with both routable and non-routable addresses will 
discard the non-routable IP addresses, and a list with rfc1918 and 
localhost addresses will discard the localhost addresses. 

== csv2_tilde_handling ==

How the csv2 zone file parser handles tildes (the ~ character) in csv2 
zone files. This is a numeric record, with a possible value between 0 
and 3 (four possible values). The way the csv2 parser acts at different 
csv2_tilde_handling levels: 

* 0) The csv2 parser behaves the same as it does in old MaraDNS 
  releases: The tilde has no special significance to the parser.

* 1) A tilde is not allowed anywhere in a csv2 zone file.

* 2) A tilde is only allowed between records in a csv2 zone file. If a 
  tilde is between the first record and the second record, a tilde 
  is required to be between all records. Otherwise, a tilde is not 
  allowed anywhere in a csv2 zone file. The first record can not be 
  a TXT, WKS, or LOC record.

* 3) A tilde is required to be between all records in a csv2 zone 
  file.

The default value for csv2_tilde_handling is 2; this allows 
compatibility with older zone files without tildes while allowing zone 
files to be updated to use the tilde to separate resource records. 

== debug_msg_level ==

This is a number indicating what level of information about a running 
MaraDNS process should be made public. When set to 0, no information 
will be made public. 

When set to one (the default), or higher, a Tversion.maradns. (TXT 
query for "version.maradns.") query will return the version number of 
MaraDNS. 

When set to two or higher, a Tnumthreads.maradns. (TXT query for 
"numthreads.maradns.") query will return the number of threads that 
MaraDNS is currently running, and a Tcache-elements.maradns. query will 
return the number of elements in MaraDNS' cache. 

If MaraDNS is compiled with debugging information on, a 
Tmemusage.maradns. query will return the amount of memory MaraDNS has 
allocated. Note that the overhead for tracking memory usage is 
considerable and that compiling MaraDNS with "make debug" will greatly 
slow down MaraDNS. A debug build of MaraDNS is not recommended for 
production use. 

When set to three or higher, a Ttimestamp.maradns. query will return, 
in seconds since the UNIX epoch, the timestamp for the system MaraDNS 
is running on.

== default_rrany_set ==

This variable used to determine what kind of resource records were 
returned when an ANY query was sent. In MaraDNS, the data structures 
have since been revised to return any resource record type when an ANY 
query is sent; this variable does nothing, and is only here so that old 
MaraDNS mararc files will continue to work. The only accepted values 
for this variable were 3 and 15. 

== dns_port ==

This is the port that MaraDNS listens on. This is usually 53 (the 
default value), but certain unusual MaraDNS setups (such as when 
resolving dangling CNAME records on but a single IP) may need to have a 
different value for this. 

== dos_protection_level ==

If this is set to a non-zero value, certain features of MaraDNS will be 
disabled in order to speed up MaraDNS' response time. This is designed 
for situations when a MaraDNS server is receiving a large number of 
queries, such as during a denial of service attack. 

This is a numeric variable; its default value is zero, indicating that 
all of MaraDNS' normal features are enabled. Higher numeric values 
disable more features: 

* A dos_protection_level between 1 and 78 (inclusive) disables getting 
  MaraDNS status information remotely.

* A dos_protection_level of 8 or above disables CNAME lookups.

* A dos_protection_level or 12 or above disables delegation NS 
  records.

* A dos_protection_level of 14 or above disables ANY record 
  processing.

* A dos_protection_level of 18 or above disables star record 
  processing at the beginning of hostnames (default zonefiles still 
  work, however).

* A dos_protection_level of 78 disables all authoritative processing, 
  including default zonefiles.

The default level of dos_protection_level is 0 when there are one or 
more zonefiles; 78 when there are no zone files. 

== ipv6_bind_address ==

If MaraDNS is compiled with as an authoritative server, then this 
variable will tell MaraDNS which ipv6 address for the UDP server to; 
for this variable to be set, MaraDNS must be bound to at least one ipv4 
address. 

== hide_disclaimer ==

If this is set to "YES", MaraDNS will not display the legal disclaimer 
when starting up. 

== long_packet_ipv4 ==

This is a list of IPs which we will send UDP packets longer than the 
512 bytes RFC1035 permits if necessary. This is designed to allow 
zoneserver, when used send regular DNS packets over TCP, to receive 
packets with more data than can fit in a 512-byte DNS packet. 

This variable only functions if MaraDNS is compiled as an authoritative 
only server. 

== maradns_uid ==

maradns_uid: The numeric UID that MaraDNS will run as 

This accepts a single numerical value: The UID to run MaraDNS as. 

MaraDNS, as soon as possible drops root privileges, minimizing the 
damage a potential attacker can cause should there be a security 
problem with MaraDNS. This is the UID maradns becomes. 

The default UID is 707. 

== maradns_gid ==

maradns_gid: The numeric GID that MaraDNS will run as. 

This accepts a single numerical value: The GID to run MaraDNS as. 

The default GID is 707. 

== max_ar_chain ==

max_ar_chain: The maximum number of records to display if a record in 
the additional section (e.g., the IP of a NS server or the ip of a MX 
exchange) has more than one value. 

This is similar to max_chain, but applies to records in the 
"additional" (or AR) section. 

Due to limitations in the internal data structures that MaraDNS uses to 
store RRs, if this has a value besides one, round robin rotates of 
records are disabled. 

The default value for this variable is 1. 

== max_chain ==

max_chain: The maximum number of records to display in a chain of 
records. 

With DNS, it is possible to have more than one RR for a given domain 
label. For example, "example.com" can have, as the A record, a list of 
multiple ip addresses. 

This sets the maximum number of records MaraDNS will show for a single 
RR. 

MaraDNS normally round-robin rotates records. Hence, all records for a 
given DNS label (e.g. "example.com.") will be visible, although not at 
the same time if there are more records than the value allowed with 
max_chain 

The default value for this variable is 8. 

== max_tcp_procs ==

max_tcp_procs: The (optional) maximum number of processes the zone 
server is allowed to run. 

Sometimes, it is desirable to have a different number of maximum 
allowed tcp processes than maximum allowed threads. If this variable is 
not set, the maximum number of allowed tcp processes is "maxprocs". 

== max_total ==

max_total: The maximum number of records to show total for a given DNS 
request. 

This is the maximum total number of records that MaraDNS will make 
available in a DNS reply. 

The default value for this variable is 20. 

== max_mem ==

max_mem is the maximum amount of memory we allow MaraDNS to allocate, 
in bytes.

The default value of this is to allocate 2 megabytes for MaraDNS' 
general use, and in addition, to allocate 3072 bytes for each element 
we can have in the cache or DNS record that we are authoritatively 
serving. 

== min_visible_ttl ==

min_visible_ttl: The minimum value that we will will show as the TTL 
(time to live) value for a resource record to other DNS servers and 
stub resolvers. In other words, this is the minimum value we will ask 
other DNS server to cache (keep in their memory) a DNS resource record. 

The value is in seconds. The default value for this is 30; the minimum 
value this can have is 5. 

As an aside, RFC1123 section 6.1.2.1 implies that zero-length TTL 
records should be passed on with a TTL of zero. This, unfortunately, 
breaks some stub resolvers (such as Mozilla's stub resolver). 

== remote_admin ==

remote_admin: Whether we allow verbose_level to be changed after 
MaraDNS is started. 

If remote_admin is set to 1, and admin_acl is set, any and all IPs 
listed in admin_acl will be able to reset the value of verbose_level 
from any value between 0 and 9 via a TXT query in the form of 
5.verbose_level.maradns. What this will do is set verbose_query to the 
value in the first digit of the query. 

This is useful when wishing to temporarily increase the verbose_level 
to find out why a given host name is not resolving, then decreasing 
verbose_level so as to minimize the size of MaraDNS' log. 

== rfc8482 ==

If this is set to 1, MaraDNS will not allow ANY queries, sending a 
RFC8482 response if one is given to MaraDNS. If this is 0, ANY queries 
are allowed. Default value: 1 

== synth_soa_origin ==

When a CSV2 zone file doesn't have a SOA record in it, MaraDNS 
generates a SOA record on the fly. This variable determines the host 
name for the "SOA origin" (which is called the MNAME in RFC1035); this 
is the host name of the DNS server which has the "master copy" of a 
given DNS zone's file. 

This host name is in human-readable format without a trailing dot, 
e.g.:

synth_soa_origin = "ns1.example.com"

If this is not set, a synthetic SOA record will use the name of 
the zone for the SOA origin (MNAME) field.

== synth_soa_serial ==

This determines whether we strictly follow RFC1912 section 2.2 with SOA 
serial numbers. If this is set to 1 (the default value), we do not 
strictly follow RFC1912 section 2.2 (the serial is a number, based on 
the timestamp of the zone file, that is updated every six seconds), but 
this makes it so that a serial number is guaranteed to be automatically 
updated every time one edits a zone file. 

If this is set to 2, the SOA serial number will be in YYYYMMDDHH 
format, where YYYY is the 4-digit year, MM is the 2-digit month, DD is 
the 2-digit day, and HH is the 2-digit hour of the time the zone file 
was last updated (GMT; localtime doesn't work in a chroot() 
environment). While this format is strictly RFC1912 compliant, the 
disadvantage is that more than one edit to a zone file in an hour will 
not update the serial number. 

I strongly recommend, unless it is extremely important to have a DNS 
zone that generates no warnings when tested at dnsreport.com, to have 
this set to 1 (the default value). Having this set to 2 can result in 
updated zone files not being seen by slave DNS servers. 

Note that synth_soa_serial can only have a value of 1 on the native 
Windows port. 

On systems where time_t is 32-bit, MaraDNS will always act as if 
synth_soa_serial has a value of 1. This is to avoid having MaraDNS use 
invalid time and date values starting in late January of 2038; systems 
with a 32-bit time_t can very well have their underlying system 
libraries with regards to dates and times no longer correctly function 
come 2038. 

== tcp_convert_acl ==

This only applies to the zoneserver (general DNS-over-TCP) program. 

This is a list of IPs which are allowed to connect to the zoneserver 
and send normal TCP DNS requests. The zoneserver will convert TCP DNS 
requests in to UDP DNS requests, and send the UDP request in question 
to the server specified in tcp_convert_server. Once it gets a reply 
from the UDP DNS server, it will convert the reply in to a TCP request 
and send the reply back to the original TCP client. 

Whether the RD (recursion desired) flag is set or not when converting a 
TCP DNS request in to a UDP DNS request is determined by whether the 
TCP client is on the recursive_acl list. Since MaraDNS 2.0 does not 
have recursion, the maradns daemon ignores the RD bit (Deadwood will 
not process any queries without the RD bit set). 

== tcp_convert_server ==

This only applies to the zoneserver (general DNS-over-TCP) program. 

This is the UDP server which we send a query to when converting DNS TCP 
queries in to DNS UDP servers. Note that, while this value allows 
multiple IPs, all values except the first one are presently ignored. 

== timestamp_type ==

timestamp_type: The type of timestamp to display. The main purpose of 
this option is to suppress the output of timestamps. Since duende uses 
syslog() to output data, and since syslog() adds its own timestamp, 
this option should be set to 5 when maradns is invoked with the duende 
tool. 

This option also allows people who do not use the duende tool to view 
human-readable timestamps. This option only allows timestamps in GMT, 
due to issues with showing local times in a chroot() environment. 

This can have the following values:  

0 The string "Timestamp" followed by a UNIX timestamp 

1 Just the bare UNIX timestamp 

2 A GMT timestamp in the Spanish language 

3 A (hopefully) local timestamp in the Spanish language 

4 A timestamp using asctime(gmtime()); usually in the English 
language 

5 No timestamp whatsoever is shown (this is the best option when 
maradns is invoked with the duende tool). 

6 ISO GMT timestamp is shown 

7 ISO local timestamp is shown  

On systems where time_t is 32-bit, MaraDNS will always act as if 
timestamp_type has a value of 5, never showing a timestamp. This is to 
avoid having MaraDNS show an invalid timestamp starting in late January 
of 2038; systems with a 32-bit time_t can very well have their 
underlying system libraries with regards to dates and times no longer 
correctly function come 2038. 

The default value for this variable is 5. 

== verbose_level ==

verbose_level: The number of messages we log to stdout 

This can have five values:  

0 No messages except for the legal disclaimer and fatal parsing 
errors 

1 Only startup messages logged (Default level) 

2 Error queries logged 

3 All queries logged 

4 All actions adding and removing records from the cache logged

The default value for this variable is 1. 

== zone_transfer_acl ==

zone_transfer_acl: List of ips allowed to perform zone transfers with 
the zone server 

The format of this string is identical to the format of an ipv4_alias 
entry. 

=== EXAMPLE MARARC FILE ===

# Example mararc file (unabridged version) 
 
# The various zones we support 
 
# We must initialize the csv2 hash, or MaraDNS will be unable to 
# load any csv2 zone files 
csv2 = {} 
 
# This is just to show the format of the file 
#csv2["example.com."] = "db.example.com" 
 
# The address this DNS server runs on.  If you want to bind  
# to multiple addresses, separate them with a comma like this: 
# "10.1.2.3,10.1.2.4,127.0.0.1" 
ipv4_bind_addresses = "127.0.0.1" 
# The directory with all of the zone files 
chroot_dir = "/etc/maradns" 
# The numeric UID MaraDNS will run as 
maradns_uid = 99 
# The (optional) numeric GID MaraDNS will run as 
# maradns_gid = 99 
 
# Normally, MaraDNS has some MaraDNS-specific features, such as DDIP 
# synthesizing, a special DNS query ("erre-con-erre-cigarro.maradns.org."  
# with a TXT query returns the version of MaraDNS that a server is  
# running), unique handling of multiple QDCOUNTs, etc.  Some people  
# might not like these features, so I have added a switch that lets  
# a sys admin disable all these features.  Just give "no_fingerprint"  
# a value of one here, and MaraDNS should be more or less  
# indistinguishable from a tinydns server. 
no_fingerprint = 0 
 
# These constants limit the number of records we will display, in order 
# to help keep packets 512 bytes or smaller.  This, combined with round_robin 
# record rotation, help to use DNS as a crude load-balancer. 
 
# The maximum number of records to display in a chain of records (list 
# of records) for a given host name 
max_chain = 8 
# The maximum number of records to display in a list of records in the 
# additional section of a query.  If this is any value besides one, 
# round robin rotation is disabled (due to limitations in the current 
# data structure MaraDNS uses) 
max_ar_chain = 1 
# The maximum number of records to show total for a given question 
max_total = 20 
 
# The number of messages we log to stdout 
# 0: No messages except for fatal parsing errors and the legal disclaimer 
# 1: Only startup messages logged (default) 
# 2: Error queries logged 
# 3: All queries logged (but not very verbosely right now) 
verbose_level = 1 
 
# Here is a ACL which restricts who is allowed to perform zone transfer from  
# the zoneserver program 
 
# Simplest form: 10.1.1.1/24 (IP: 10.1.1.1, 24 left bits in IP need to match) 
# and 10.100.100.100/255.255.255.224 (IP: 10.100.100.100, netmask 
# 255.255.255.224) are allowed to connect to the zone server  
# NOTE: The "maradns" program does not serve zones.  Zones are served 
# by the "zoneserver" program. 
#zone_transfer_acl = "10.1.1.1/24, 10.100.100.100/255.255.255.224"

=== BUGS ===

If one should declare the same the same index twice with a dictionary 
variable, MaraDNS will exit with a fatal error. This is because earlier 
versions of MaraDNS acted in a different manner than Python 2.3.3. With 
Python 2.3.3, the last declaration is used, while MaraDNS used to use 
the first declaration. 

=== LEGAL DISCLAIMER ===

THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR 
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.  

