#
# $Id$
#
#
# Applicability of this Configuration File
# ----------------------------------------
#
# This is default SER script as used for example at the iptel.org
# SIP service; it can deal with NATs, terminate calls to a PSTN
# gateway, and it implements a couple of basic signaling features
# (few types of call forwarding). In this scenario you may have
# multiple SIP proxies sharing one database for accessing provisioned
# data, which are maintained for example using serweb. The proxy
# servers also share write-access to user location database (and
# keeps a full cache of all usrloc entries synchronized using
# multicast).
#
# If you look for a simpler version with a lot less dependencies
# please refer to the sip-router-basic.cfg file in your SER distribution.
#
# Requirements:
# ---------------
# running DB, running RTP proxy, one public IP address
# for SIP service, one private IP address for administrative purposes;
# optional: IP address of a PSTN gateway
#
# HOWTOs:
# ---------
# To get this config running you need to execute the following commands
# with the new serctl (the capital word are just place holders):
#
#    $ ser_ctl domain add DOMAINNAME
#    $ ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD
#
# If you want to have P-Asserted-ID header for your user
#
#    $ ser_attr add uid=UID asserted_id="PID"
#
# If you want to have (PSTN) gateway support:
#
#    $ ser_db add attr_types name=gw_ip rich_type=string raw_type=2 \
#      description="The gateway IP for the default ser.cfg" default_flags=33
#    $ ser_attr add global gw_ip=GATEWAY-IP
#
# Alternatively, you can simple uncomment the relevant line in this file
# right at the beginning of the main route.
#
# You can also use serweb to set all the values above.
#
# Users with permission to call PSTN using this script must have
# the $gw_acl attribute set properly, and shall have $asserted_id
# set to indicate their caller-id for calls to PSTN. For inbound
# calls from PSTN, additional aliases may be also set.
#
# Warning:
# -----------
# If this file is installed on Debian from package 'ser-oob' then some
# options in this configuration file may be set by post-installation
# script, according to values entered by user at installation time in
# debconf configuration. These values are then applied automatically to
# this file each time the 'ser-oob' package is upgraded or reconfigured by
# calling 'dpkg-reconfigure sip-router-oob'.
#
# The parts of this configuration file that may be altered by debconf are
# enclosed between '#DEBCONF-something-START' and '#DEBCONF-something-END'
# comment marks. Please do not remove them.
#
#
# TODO (Future possible improvements):
# ---------------------------------------
# * protocol tuning
#   - AVP-based diversion for call-forwarding (as opposed to specialized
#     module)
#   - add Date header in 200s to REGISTERs (to be packaged with NTP!)
# * more security:
#   - pike/rate-limit
#   - identity
#   - TLS
#   - permissions
#   - Re-name all internal headers so that they start with a common prefix,
#     such as P-SER and then wipe all such headers from requests received
#     from untrusted sources, such as the user agents or foreign proxy
#     servers
# * refined DB use (e.g., flatstore for acc)
# * miscellanous:
#  - dialog module for monitoring purposes
#  - more extensive logging using xlog (controlled by gflags/gAVPs)
# * leveraging 2.1 features:
#  - removal of private IP address (it takes a multicast-specific
#    command which will allow OS to determine source IP address)
#  - timer route:
#     * don't use exec (it takes domain.reload as script command)
#     * compare last-stored timestamp with current timestamp (it takes
#       assignment of gAVPs)
#     * check multicast REGISTERs for their TTL (this is a simple and
#       effective security check to prevent remote multicast messages
#       to damage our traffic)
#  - numerous fine-tuning parameters which are only available in 2.1
#   (mlock_pages, dns_try_naptr, etc.)
#  - better support for preloaded routes with domain name
#
# Security considerations:
# ------------------------
# The script has been tested against security leaks, but it comes
# under terms of GPL "as is" without any warranties; better check
# yourself that:
# - IP based authentication of PSTN gateway and multicast REGISTERs
#   is compliant to your network setup and security policy.
# - Multiple gateway IPs can't be provisioned as security checks
#   are applied only to one.
#
# Licensing
# ----------
# Copyright (C) 2005-2008 iptelorg GmbH
# This file is part of SER, a free SIP server. It is available under the
# terms of the  GNU General Public License.
# Numerous folks have contributed to this file, including but not limited
# to Andrei, Jan, Jiri, Michal, Miklos, Nils.
#
#
# .... that's it, enough of yadiyada, here the real config begins!


# ----------- Global Configuration Parameters -------------------------------

#debug=3    # debug level (cmd line: -ddd)
#memdbg=10  # memory debug log level
#memlog=10  # memory statistics log level
#log_facility=LOG_LOCAL0 # the facility used for logging (see syslog(3))

#DEBCONF-SERVERID-START
server_id=0
#DEBCONF-SERVERID-END

# Uncomment these lines to enter debugging mode or start SER with
# sip-router -ED
#
#fork=no
#log_stderror=yes

check_via=no            # (cmd. line: -v)
dns=no                  # (cmd. line: -r)
rev_dns=no              # (cmd. line: -R)
#port=5060
#children=4
#user=sip-router
#group=sip-router
#disable_core=yes       # disables core dumping
#open_files_limit=20480  # sets the open file descriptors limit
#mhomed=yes             # usefull for multihomed hosts, small performance
                        # penalty
disable_tcp=no          # be conservative about enabling TCP -- it can
                        # degrade performance a lot
#tcp_accept_aliases=yes # accepts the tcp alias via option
phone2tel=no            # ignore user=phone in request-URIs -- otherwise
                        # these URIs would be interpreted as equivalent
			# to TEL URIs, and their lookup would fail in URI
			# database
reply_to_via=no
# public IP address
#DEBCONF-LISTEN-START
listen=127.0.0.1
#DEBCONF-LISTEN-END
# sip.mcast.net for REGISTER replication 	 
#DEBCONF-LISTEN_REPL-START 	 
listen=224.0.1.75 	 
#DEBCONF-LISTEN_REPL-END
# administrative interface -- needed for example for multicast source
# or XML-RPC
#DEBCONF-LISTEN_ADMIN-START
listen=udp:127.0.0.1
#DEBCONF-LISTEN_ADMIN-END

#listen=tls:127.0.0.1:5061

mlock_pages=yes
shm_force_alloc=yes
real_time=7

# ------------------- DNS Parameters ----------------------------------------
# (see doc/dns.txt for more details)
#
# minimum timeouts
dns_retr_time=1
dns_retr_no=1
dns_servers_no=1
dns_use_search_list=no
dns_try_ipv6=no
# dns cache & failover
use_dns_cache=on
use_dns_failover=on
# dns_cache_flags=0
dns_cache_negative_ttl=300
dns_cache_min_ttl=60
dns_cache_max_ttl=86400 # 1 day
dns_cache_mem=2048 # 2 MB
dns_cache_gc_interval=60  # garbage collection every minute
# ser 2.1 specific options
dns_try_naptr=yes
dns_srv_lb=yes  # srv based load balancing
dns_udp_pref=3  # prefer udp (when resolving naptr record)
dns_tcp_pref=2  # if no udp availbale accept tcp (for naptr)
dns_tls_pref=-1 # ignore / don't accept tls (for naptr)
# dns_cache_delete_nonexpired=no

# ------------------- Blacklist Parameters ----------------------------------
# (see doc/dst_blacklist.txt for more details)
#
use_dst_blacklist=on
dst_blacklist_mem=1024 # 1 MB
dst_blacklist_expire=300  # blacklist default time
dst_blacklist_gc_interval=150 # 2.5 min
# for sip-router 2.1 to the above add tm blst_503* parameters and/or use the
# blst module (see NEWS)

# ------------------- TCP Parameters ----------------------------------------
# (see NEWS for more details)
tcp_connection_lifetime=3600
#tcp_max_connections=10240  # default is 2048
tcp_connect_timeout=1

# ------------------- TLS Parameters ----------------------------------------

# Enable TLS hooks so that the TLS module can be used
tls_enable=yes
# This option is required if you want to use TLS as the TLS
# module does not support the new async TCP mode yet
tcp_async=no

# -------------------- Custom Parameters ------------------------------------
# These parameters can be modified runtime via RPC interface,
# read the documentation of cfg_rpc module.

# Session Timer parameters, RFC 4028
#
# Default session interval used by the proxy if the UAC does not support
# session timer. Set it to "0" to disable session timer proxy support.
#
session_timer.default = "1800" desc "default session interval (in s)"
#
# Minimum session interval accepted by the proxy, it must not be less
# than 90 seconds.
#
session_timer.min_se = "90" desc "minimum session interval (in s)"

# RTP Proxy options
#
# Whether to enable or disable the rtp proxy. Possible values are:
# "0" -- always disable
# "1" -- always enable regardless of whether UAC or UAS is behind NAT
# "detect" -- detect whether the UAC or the UAS is behind NAT,
#             and enable the rtp proxy when necessary
#
#DEBCONF-RTP_ENABLE-START
rtp_proxy.enabled = "detect" desc "indicates whether the RTP Proxy is enabled or not (0/1/detect)"
#DEBCONF-RTP_ENABLE-END

# ------------------ Module Loading -----------------------------------------

#loadpath "modules:modules_s"
loadpath "/usr/lib/sip-router/modules:/usr/lib/sip-router/modules_s"

# load a SQL database for authentication, domains, user AVPs etc.
loadmodule "db_mysql"
#loadmodule "postgres"

loadmodule "sl"
loadmodule "tm"
loadmodule "rr"
loadmodule "maxfwd"
loadmodule "usrloc"
loadmodule "registrar"
loadmodule "xlog"
loadmodule "textops"
loadmodule "ctl"
loadmodule "auth"
loadmodule "auth_db"
loadmodule "gflags"
loadmodule "domain"
loadmodule "uri_db"
loadmodule "avp"
loadmodule "avp_db"
loadmodule "acc_db"
#loadmodule "xmlrpc"
loadmodule "options"
loadmodule "sanity"
loadmodule "nathelper"
loadmodule "uri"
loadmodule "speeddial"
loadmodule "timer"
loadmodule "db_ops"
loadmodule "exec"
loadmodule "cfg_rpc"
loadmodule "eval"
loadmodule "enum"
#loadmodule "tls"

# ----------------- Declaration of Script Flags -----------------------------
flags
  FLAG_ACC            : 1, # the request will be recorded by ACC
  FLAG_FAILUREROUTE   : 2, # we are operating from the failure route
  FLAG_NAT            : 3, # the UAC is behind a NAT
  FLAG_REPL_ENABLED   : 4, # REGISTER replication is enabled if set
  FLAG_TOTAG          : 5, # request has a To tag
  FLAG_PSTN_ALLOWED   : 6, # the user is allowed to use the PSTN
  FLAG_DONT_RM_CRED   : 7, # do not remove the credentials
  FLAG_AUTH_OK        : 8, # authentication succeeded
  FLAG_SERWEB_RSVD1   : 9, # bit reserved for use with serweb
  FLAG_SERWEB_RSVD2   : 10, # bit reserved for use with serweb
  FLAG_SESSIONTIMER   : 11, # indicates that the UAC supports Session Timer
  FLAG_RR_DONE        : 12, # the request got already one RR header
  FLAG_RTP_PROXY      : 13; # the RTP proxy is turned on

avpflags
  dialog_cookie;            # attribute will be stored in Route headers

# ----------------- Module-specific Parameters ------------------------------

# path to the database
#
#DEBCONF-DBURL-START
modparam("speeddial|auth_db|usrloc|domain|uri_db|gflags|avp_db|db_ops",
         "db_url", "mysql://sip-router:heslo@127.0.0.1/sip-router")
#DEBCONF-DBURL-END

# specify the path to your database for accounting
#DEBCONF-DBURLACC-START
modparam("acc_db", "db_url", "mysql://sip-router:heslo@127.0.0.1/sip-router")
#DEBCONF-DBURLACC-END


# -- usrloc --

# Database access mode: 0 -- memory cached, 1 -- write through,
# 2 -- delayed write.  1 is generally safer than 2.  2 can help
# to survive peaks in load.  However, it creates delayed peaks that can
# impair request-processing latency later (usrloc would have to be
# re-redesigned more lock-free to avoid it).
#DEBCONF-DBMODE-START
modparam("usrloc", "db_mode", 1)
#DEBCONF-DBMODE-END

# Don't delete expired records from database on a per-contact basis -- that
# results in bulky DB operations and can lead to synchronization issues
# in server farm when for a time a server doesn't obtain re-reregistrations
modparam("usrloc","db_skip_delete",1)


# -- registrar --

# Maximum expires time.  Forces users to re-register every 10 min.
modparam("registrar", "max_expires", 600)

# Minimum expires time. Even if they try, clients cannot register
# for a shorter time than this.
modparam("registrar", "min_expires", 240)

# Identify natted contacts using a flag.
modparam("registrar", "load_nat_flag", "FLAG_NAT")
modparam("registrar", "save_nat_flag", "FLAG_NAT")

# Maximum number of contacts.
modparam("registrar", "max_contacts", 10)


# -- auth --

#modparam("auth_db", "calculate_ha1", yes)
#modparam("auth_db", "password_column", "password")

# Minimize replay-attack window.
modparam("auth", "nonce_expire", 10)

# Enable/disable extra authentication checks using the following modparams.
# The values are: 1 -- Request-URI, 2 -- Call-ID, 4 -- From tag,
# 8 -- source IP. The options are disabled by default.

# For REGISTER requests we hash the Request-URI, Call-ID, and source IP of the
# request into the nonce string. This ensures that the generated credentials
# cannot be used with another registrar, user agent with another source IP
# address or Call-ID. Note that user agents that change Call-ID with every
# REGISTER message will not be able to register if you enable this.
#modparam("auth", "auth_checks_register", 11)

# For dialog-establishing requests (such as the original INVITE, OPTIONS, etc)
# we hash the Request-URI and source IP. Hashing Call-ID and From tags takes
# some extra precaution, because these checks could render some UA unusable.
#modparam("auth", "auth_checks_no_dlg", 9)

# For mid-dialog requests, such as re-INVITE, we can hash source IP and
# Request-URI just like in the previous case. In addition to that we can hash
# Call-ID and From tag because these are fixed within a dialog and are
# guaranteed not to change. This settings effectively restrict the usage of
# generated credentials to a single user agent within a single dialog.
#modparam("auth", "auth_checks_in_dlg", 15)

# Deal with clients who can't do qop properly
modparam("auth", "qop", "")
#DEBCONF-AUTHSECRET-START
modparam("auth", "secret", "aqwedrftredswqwddcft")
#DEBCONF-AUTHSECRET-END


# -- rr --

# Add value to lr param to make some broken UAs happy.
modparam("rr", "enable_full_lr", 1)

# Limit the length of the AVP cookie to necessary attributes only
modparam("rr", "cookie_filter", "(account|rproxy|stimer)")

# You probably do not want that someone can simply read and change
# the AVP cookie in your Routes, thus should really change this
# secret value below
modparam("rr", "cookie_secret", "sgsatewgdbsnmpoiewh")

# The ftag Route parameter may be used to easily determine if a BYE
# is coming from caller or callee, but we prefer shorter messages
modparam("rr", "append_fromtag", 0)


# -- gflags --

# Load global attributes.
modparam("gflags", "load_global_attrs", 1)


# -- domain --

# Load domain attributes.
modparam("domain", "load_domain_attrs", 1)


# -- ctl --

# By default, ctl listens on unixs:/tmp/sip-router_ctl if no other address is
# specified in modparams; this is also the default for sercmd.
modparam("ctl", "binrpc", "unixs:/tmp/sip-router_ctl")
# Listen on the "standard" fifo for backward compatibility.
modparam("ctl", "fifo", "fifo:/tmp/sip-router_fifo")
# Listen on tcp on localhost.
#modparam("ctl", "binrpc", "tcp:localhost:2046")


# -- acc_db --

# Failed transactions (those with negative responses) should be logged, too.
modparam("acc_db", "failed_transactions", 1)

# If you don't want to have accounting entries written into the database,
# comment the next line out.
modparam("acc_db", "log_flag", "FLAG_ACC")

# if you would like to customize your CDRs, do it here....
#modparam("acc_db", "attrs",
#         "$f.sop_billing_category,$f.isPrepaidCustomer,$f.sop_cf_orig_uid")


# -- tm --

# Do not restart the resend timer with each reply. (See INBOUND route
# below.)
modparam("tm", "restart_fr_on_each_reply", 0)


# -- xmlrpc --

# Use a sub-route. This is a lot safer then relying on the request method
# to distinguish HTTP from SIP
#modparam("xmlrpc", "route", "RPC");


# -- nathelper --

# RTP Proxy address
#DEBCONF-RTTPPROXY-START
modparam("nathelper", "rtpproxy_sock", "udp:127.0.0.1:22222")
#DEBCONF-RTTPPROXY-END

# TCP keepalives as simple as CRLF
modparam("nathelper", "natping_crlf", 0)

# How often to send a NAT ping. Set this to 0 to turn NAT ping off.
#DEBCONF-NATPING_INTERVAL-START
modparam("nathelper", "natping_interval", 15)
#DEBCONF-NATPING_INTERVAL-END

# Only ping contacts that have the NAT flag set.
modparam("nathelper", "ping_nated_only", 1)

# Send an OPTIONS SIP request as NAT ping. If this is not set, a simple
# 4-byte ping is used.
modparam("nathelper", "natping_method", "OPTIONS")

# Temporary statefull natping test (only in future versions)
#modparam("nathelper", "natping_stateful", 1)


# -- exec --
modparam("exec", "time_to_kill", 200);
modparam("exec", "setvars", 0);

# -- timer --

# Register route ON_1MIN_TIMER to be called every minute.
modparam("timer", "declare_timer",
         "ON_1MIN_TIMER=ON_1MIN_TIMER,60000,slow,enable");

# -- tls --
#modparam("tls", "config", "tls.cfg");

# -- db_ops --

modparam("db_ops", "declare_handle", "reload")
modparam("db_ops", "declare_handle", "gattr_reload")


# -------------------------  Request Routing Logic --------------------------

# Main request route.
#
# Each request starts here.
#
route
{
	# if you have a PSTN gateway just un-comment the follwoing line and
	# specify the IP address of it to route calls to it.
	#$gw_ip = "1.2.3.4"

	# Alternatively (even better), set it as global persistent parameter
	# using serweb or ser_attrs). If using a PSTN GW, per-subscriber
	# options must ($gw_acl) or may (asserted_id) be set to enable calls
	# to PSTN. If email-like URIs are used, having a URI alias for
	# processing incoming PSTN-to-ip requests may be useful, too.
	# Important: the script is assuming one global pstn-gw for all
	# domains! Failure to allow gw_ip to be a domain-specic attribute
	# would result in security gaps (onsend_route checks only for one
	# gateway).

	# First, do some initial sanity checks.
	route(INIT);

	# Bypass the rest of the script for CANCELs if possible.
	route(CATCH_CANCEL);

	# Check if the request is routed via Route header.
	route(PROCESS_ROUTES);

	# Look up domain IDs
	route(DOMAIN);

	# Answer OPTIONS requests to our system.
	route(OPTIONS_REPLY);

	# Enforce domain policy.
	route(DOMAIN_POLICY);

	# Handle REGISTER requests.
	route(REGISTRAR);

	# From here on we want to know who is calling.
	route(AUTHENTICATION);

	# We are finished with all the precaution work -- let's
	# try to locate the callee. The first route that matches
	# "wins" and relays the request.  If none matches, SER will
	# send a 404.

	# Check if we should be outbound proxy for a local user.
	route(OUTBOUND);

	# Redirect in case user dialed a speed dial entry.
	route(SPEEDDIAL);

	# Place various site-specific routes here.
	route(SITE_SPECIFIC);

	# Check if the request is for a local user.
	route(INBOUND);

	# There is SIP user for the called address. Before trying PSTN,
	# you may have to convert the adress, for instance by using
	# ENUM.
	#route(ENUM);

	# Last resort: if none of the previous route has found
	# the recepient, try PSTN.
	route(PSTN);

	# nothing matched
	sl_reply("404", "No route matched");
}

# Forward a request to the destination set.
#
route[FORWARD]
{
	# If this is called from the failure route we need to add a new
	# branch.
	if (isflagset(FLAG_FAILUREROUTE)) {
		if (!append_branch()) {
			t_reply("500", "Too many branches");
			drop;
		}
	}

	# If this is an initial INVITE (without a To-tag) we might try
	# another target (call forwarding or voicemail) after receiving
	# an error.
	if (method=="INVITE" && strempty(@to.tag)) {
		t_on_failure("FAILURE_ROUTE");
	}

	# Always use the reply route to check for NATed UAS.
	t_on_reply("REPLY_ROUTE");

	# Remove credentials to keep requests shorter
	if (isflagset(FLAG_AUTH_OK) && !isflagset(FLAG_DONT_RM_CRED) ) {
		consume_credentials();
	}

	# Activate the RTP proxy as the second last step because it modifies the
	# body but also sets an dialog AVP cookie.
	route(RTPPROXY);

	# Insert a Record-Route header into all requests.
	# This has to be done as one of the last steps to include all the
	# RR cookies which might have been created during the script run.
	route(RECORD_ROUTE);

	# Send it out now.
	if (!t_relay()) {
		if (isflagset(FLAG_FAILUREROUTE)) {
			# XXX This should be replaced with
			#     t_reply_error() similar to sl_reply_error()
			#     in order to return the proper failure code.
			#     Only, there is no such function yet.
			t_reply("500", "Request cannot be forwarded");
		}
		else {
			sl_reply_error();
		}
	}
	drop;
}


# Perform initial checks on an incoming request.
#
# Rejects the request if it fails any of the checks.
#
route[INIT]
{
	# Messages with a Max-Forwards header of zero.
	if (!mf_process_maxfwd_header("10")) {
		sl_reply("483","Too Many Hops");
		drop;
	}

	# Set flag for use in the onsend route (because it does not
	# allow to use "select" statements)
	if (@to.tag != "") {
		setflag(FLAG_TOTAG);
	}

	# Check if the UAC is NATed and fix the message accordingly
	route(UAC_NAT_DETECTION);

	# Activate accounting for all initial INVITEs. In-dialog requests
	# are accounted by a RR cookie (see below).
	if (method == "INVITE" && strempty(@to.tag)) {
		setflag(FLAG_ACC);
	}

	# Set flag and use it instead of the attribute.
	if ($replicate==1) {
		setflag(FLAG_REPL_ENABLED);
	}
}


# Reply OPTIONS requests sent to the proxy itself.
#
route[OPTIONS_REPLY]
{
	# OPTIONS requests without a username in the Request-URI but one
	# of our domains or IPs are addressed to the proxy itself and
	# can be answered statelessly.
	if (method == "OPTIONS" && strempty(@ruri.user) &&
		(uri == myself || $t.did != ""))
	{
		options_reply();
		drop;
	}
}


# Check if the sender of the request is behind a NAT device. If so,
# fix the request so that other devices can talk to the sender none the less.
#
route[UAC_NAT_DETECTION]
{
	# Lots of UAs do not include the rport parameter in there Via
	# header, so we put it there regardless.
	force_rport();

	# If a reliable transport was used store the connection internally
	# so that SERs core can re-use the connection later.
	if (proto==TCP || proto == TLS)
	{
		force_tcp_alias();
	}

	# Check if the request contains hints for a NATed UAC. Also, try to
	# rewrite contacts using maddr. Using maddr is a really dubious
	# technique and we better replace such with transport address.
	# Downside: it fails for clients fronted by another server, in
	# which case a valid contact we dislike because of maddr will be
	# substituted inapproprietely (e.g., WM from other domains will
	# fail). If you are worried about that, remove tests for maddr and
	# recompile SER using HONOR_MADDR.  Also note that rewriting
	# contacts may possibly lead to client denying subseqent requests
	# to them because they don't recognized fixed contacts as their
	# own.  Should you encounter such a case, a possible solution
	# would be to store the original information as a contact parameter
	# and restore it on its way back.

	# In case of UDP we test for
	#  - private IPs in Contact
	#  - mismatch of transport IP and IP in Via
	#  - mismatch of transport port and port in Via
	# in all other cases we skip the port test, because lots of clients
	# do not correctly advertise their emphemeral port number in their Via
	# header in case of reliable transports (although they are not behind
	# a NAT).

	# Warning: if you are dealing with SIP implementations which are
	# running on public IP and do as-symmertic signaling for whatever
	# reason the following check will make their signaling symmetric.
	# If you need to support as-symmertic signaling reduce the following
	# nat_uac_test for UDP to "3" or even "1".
	if ((proto == UDP && nat_uac_test("19")) ||
		(nat_uac_test("3")) ||
		(@hf_value["contact"] != "" && @contact.uri.params.maddr != ""))
	{
		setflag(FLAG_NAT);
		if (method == "REGISTER") {
			# Prepare the Contact so that the registrar module
			# saves the source address and port as well.
			fix_nated_register();
		}
		else {
			# Overwrite the Contact to allow proper in-dialog
			# routing.
			fix_nated_contact();
		}
	}
}


# Check if the receiver of the request is behind a NAT device. If so,
# fix the Contact header to allow proper routing of in-dialog requests.
route[UAS_NAT_DETECTION]
{
	# Fix the Contact in the reply if it contains a private IP to
	# allow proper routing of in-dialog messages.
	# Do the same if the contact is maddred.

	# But skip 3XX responses, because we do not know the right IP for that,
	# even if they contain private IPs.
	if (status=~"(3[0-9][0-9])") {
		break;
	}

	# Prevent that we over-write the Contact with the IP of our proxy when
	# the reply loops through ourself.
	if (src_ip == myself) {
		break;
	}

	# In this case we check only if the Contact URI contains a private
	# IP, because the Via header contains only informations from the UAC.
	# Additionally we check if the port in the Contact URI differs from
	# the port of the transport to catch UAS or ALG which put the public
	# IP address into the Contact header, but "forget" about the port.

	# Warning: if you are dealing with SIP implementations which are
	# running on public IP and do as-symmertic signaling for whatever
	# reason the following check will make their signaling symmetric.
	# If you need to support as-symmertic signaling reduce the following
	# nat_uac_test for UDP to just "1".
	if ( (proto == UDP && nat_uac_test("33")) ||
		(nat_uac_test("1") ||
		(@hf_value["contact"] != "" && @contact.uri.params.maddr != "")))
	{
		fix_nated_contact();
	}
}


# Activates RTP proxy if necessary.
#
route[RTPPROXY]
{
	if (@cfg_get.rtp_proxy.enabled == "0") {
		# RTP Proxy is disabled
		break;
	} else if (@cfg_get.rtp_proxy.enabled == "detect") {
		if (!isflagset(FLAG_NAT)) {
			# If no NAT is involved we don't have to do here anything.
			break;
		}
	} else if (@cfg_get.rtp_proxy.enabled != "1") {
		# This is not a valid setting
		xlog("L_ERR", "Unknown option for rtp_proxy.enabled: %@cfg_get.rtp_proxy.enabled\n");
		break;
	} # else rtp proxy is permanently enabled

	# If the message terminates a dialog for which the RTP proxy 
	# was turned on, turn it off again.
	if ((method == "BYE" && isflagset(FLAG_RTP_PROXY)) ||
		(method == "CANCEL")) {
		unforce_rtp_proxy();
		append_hf("P-RTP-Proxy: Off\r\n");
		break;
	}

	# Turn the RTP proxy on for INVITEs and UPDATEs, if they 
	# have a body
	if (((method=="INVITE" || method == "UPDATE") && @msg.body != "")
	    && !isflagset(FLAG_RTP_PROXY))
	{
		force_rtp_proxy('r');
		append_hf("P-RTP-Proxy: On\r\n");
		setflag(FLAG_RTP_PROXY);
		$rproxy = 1;
		setavpflag($rproxy, "dialog_cookie");
	}
}


# Handling of Route headers
#
route[PROCESS_ROUTES]
{
	# subsequent messages withing a dialog should take the
	# path determined by the Route headers.
	if (loose_route()) {
		# Mark routing logic in request.
		append_hf("P-hint: rr-enforced\r\n");

		# If the Route contained the accounting AVP cookie we
		# set the accounting flag for the acc_db module.
		# This is more for demonstration purpose as this could
		# also be solved without RR cookies.
		# Note: this means all in-dialog request will show up in
		# the accounting tables, so prepare your accounting software
		# for this.
		if ($account == "yes") {
			setflag(FLAG_ACC);
		}

		# Restore the RTP proxy flag if present
		if ($rproxy == "1") {
			setflag(FLAG_RTP_PROXY);
		}

		# Restore Session Timer flag and headers.
		if ( defined $stimer && ($stimer != "0")) {
			route(SESSION_TIMER);
		}

		# Some broken devices overide the dialog route set with the
		# Record-Route headers from each in-dialog request. So, we
		# better add Record-Route headers again. If we call
		# record_route() after loose_route(), the AVP cookies are
		# restored automatically. Additionally, there is a scenario
		# where Record-Route headers are necessary if an initial
		# SUBSCRIBE is forked.
		#
		# Note that here we forward before authentication checks
		# are executed. Generally, we only authenticate
		# out-of-dialog requests. Some in-dialog requests can't be
		# authenticated at all, see the call-forwarding example in
		# route[DOMAIN].
		route(RECORD_ROUTE);

		route(FORWARD);
	}
}


# Add a Record-Route header
#
route[RECORD_ROUTE]
{
	if (!isflagset(FLAG_RR_DONE) && method != "REGISTER") {
		# We record-route all messages to make sure that
		# subsequent messages will go through our proxy. This is
		# particularly good if upstream and downstream entities
		# use different transport protocols.

		# If the ACC flag is set, store this in a Record-Route
		# AVP cookie. This is more for demonstration purposes.
		if (isflagset(FLAG_ACC)) {
			$account = "yes";
			setavpflag($account, "dialog_cookie");
		}

		# Insert the RR header.
		record_route();

		# This flag allows to call this route several times
		# without inserting several RR headers.
		setflag(FLAG_RR_DONE);
	}
}


# Look up the domains of the caller and the callee.
#
route[DOMAIN]
{
	# Check whether the caller is from a local domain.
	lookup_domain("$fd", "@from.uri.host");

	# Check whether the callee is at a local domain
	lookup_domain("$td", "@ruri.host");
}


# Check domain usage policies and reject illegal requests.
#
route[DOMAIN_POLICY]
{

	# If we don't know the domain of the caller nor the domain of the
	# callee, somone tries to use our proxy as a relay.  However, we
	# can only apply this check out-of-dialog requests without a To
	# tag.  In some cases such as call-forwarding, subsequent requests
	# may not include served domain neither as origination nor
	# destination (a@A calls b@B who forwards to c@C. A BYE by c@C is
	# then From b@B and To a@A. There is no mentioning of c@C despite
	# legitimate behaviour of c@C).
	if (!isflagset(FLAG_TOTAG) && strempty($t.did) && strempty($f.did)) {
		sl_reply("403", "Relaying Forbidden");
		drop;
	}
}


# The Registrar
#
route[REGISTRAR]
{
	# Process only REGISTERs here.
	if (method != "REGISTER") {
		break;
	}

	# If this is a replica (sent to the multicast address), trust it to
	# be secure and store it in usrloc
	if (dst_ip==224.0.1.75) {
		if (!isflagset(FLAG_REPL_ENABLED)) {
			# Multicast replication administratively disabled.
			# Ignore.
			drop;
		}

		# Read marker from master
		if (search("^Repl-Marker: nated")) {
			setflag(FLAG_NAT);
		}

		# If the replicating server added its own server id to the
		# request, obtain the value and store it in an attribute.
		# This is used by registrar.
		$server_id = @msg.header["SER-Server-ID"];

		# Assume URI in form of UID@mydomain and store contacts
		# under this UID.  Note that this only works if local policy
		# causes UIDs to have form compliant to RFC3261 URI
		# usernames.
		$tu.uid = @ruri.user;
		if (!save_mem_nr("location")) {
			log(1, "Error while saving replicated REGISTER.\n");
		}
		drop;
	}
	else {
		# This is a REGISTER request received from the UA. Remove
		# our internal header fields if they are present. The may
		# have been added maliciously.
		remove_hf("SER-Server-ID");
		remove_hf("Repl-Marker");
	}

	# Check if the REGISTER if for one of our local domains.
	if (strempty($t.did)) {
		sl_reply("403", "Register Forwarding Forbidden");
		drop;
	}

	# The REGISTER target is in the To header, so reload the domain.
	if (!lookup_domain("$td", "@to.uri.host")) {
		sl_reply("404", "Unknown Domain");
		drop;
	}

	# Useful for clients that ignore expires in 200 (OK). This is an
	# attempt to keep them sticking to our value of 600.
	append_to_reply("Expires: 600\r\n");
	append_to_reply("Min-Expires: 240\r\n");

	# We want only authenticated users to be registered.
	if (!www_authenticate("$fd.digest_realm", "credentials")) {
		if ($? == -2) {
			sl_reply("500", "Internal Server Error");
		}
		else if ($? == -3) {
			sl_reply("400", "Bad Request");
		}
		else {
			if ($digest_challenge != "") {
				append_to_reply("%$digest_challenge");
			}
			sl_reply("401", "Unauthorized");
		}
		drop;
	}

	# Check if the authenticated user is the same as the target user.
	if (!lookup_user("$tu.uid", "@to.uri")) {
		sl_reply("404", "Unknown user in To");
		drop;
	}

	# the authentication ID does not match the ID in the To header
	if ($f.uid != $t.uid) {
		sl_reply("403", "Authentication and To-Header mismatch");
		drop;
	}

	# Check if the authenticated user is the same as the request
	# originator. You may uncomment it if you care, which URI is in
	# the From header.
	#if (!lookup_user("$fr.uid", "@from.uri")) {
	#	sl_reply("404", "Unknown user in From");
	#	drop;
	#}
	#if ($fu.uid != $fr.uid) {
	#	sl_reply("403", "Authentication and From-Header mismatch");
	#	drop;
	#}

	# Everything is fine. Store the binding.
	if (!save_contacts("location")) {
		sl_reply("400", "Invalid REGISTER Request");
		drop;
	}
# do not delete the following 3 lines, they are used by debconf
#DEBCONF-REPLICATION1-START
#
#DEBCONF-REPLICATION1-END
	if (isflagset(FLAG_REPL_ENABLED)) {
		if (isflagset(FLAG_NAT)) {
			append_hf("Repl-Marker: nated\r\n");
		}
		# Append this server's unique ID to the request
		append_hf_value("SER-Server-ID", "%@sys.server_id");
		# We are multicasting a successful REGISTER to all proxies
		# on the multicast network to replicate the contact
		# addresses to all of them. In case they share the same IP
		# address (VIP) it is important to set the sending IP
		# address to an unshared one (in the future a special mcast
		# module may use unbound sockets for sending and leave
		# the source IP address decision up to kernel routing
		# tables).
		#DEBCONF-REPL_SEND_ADDR-START
		force_send_socket(udp:127.0.0.1);
		#DEBCONF-REPL_SEND_ADDR-END
		# Put the UID in the Request-URI so that it doesn't have to
		# be looked up in the database by all multicast receivers.
		attr2uri("$tu.uid","user");
		forward_udp(224.0.1.75,5060);
	}
#DEBCONF-REPLICATION2-START
#
#DEBCONF-REPLICATION2-END

	drop;
}


# Authentication of request originators claiming to belong to one of our
# domains.
#
route[AUTHENTICATION]
{
	# CANCELs and ACKs cannot be challenged.
	if (method=="CANCEL" || method=="ACK") {
		break;
	}

	# Requests from non-local to local domains should be permitted.
	# Remove this if you want a walled garden.
	if (strempty($f.did)) {
		break;
	}

	# Gateways are usually not able to authenticate for their requests.
	# You have to trust them base on some other information such as the
	# source IP address.
	# WARNING: If at all this is only safe in a local network!
	if (@src.ip == $gw_ip) {
		break;
	}

	if (!proxy_authenticate("$fd.digest_realm", "credentials")) {
		if ($? == -2) {
			sl_reply("500", "Internal Server Error");
		}
		else if ($? == -3) {
			sl_reply("400", "Bad Request");
		}
		else {
			if (defined $digest_challenge && $digest_challenge != "") {
				append_to_reply("%$digest_challenge");
			}
			sl_reply("407", "Proxy Authentication Required");
		}
		drop;
	}

	# Check if the UID derived from authentication matches that from
	# the From header.
	if (!lookup_user("$fr.uid", "@from.uri")) {
		sl_reply("403", "Fake Identity");
		drop;
	}
	if ($fu.uid != $fr.uid) {
		sl_reply("403", "Fake Identity");
		drop;
	}
	setflag(FLAG_AUTH_OK);

	# Load the user attributes of the caller.
	load_attrs("$fu", "$f.uid");
}


# Process request targeted to non-local domains.
#
route[OUTBOUND]
{
	# If a local user calls to a foreign domain we play outbound
	# proxy for them.
	# Comment this out if you want a walled garden.
	if ($f.did != "" && strempty($t.did)) {
		append_hf("P-hint: outbound\r\n");
		route(FORWARD);
	}
}


# Process speeddial addresses.
#
route[SPEEDDIAL]
{
	# If the caller is local and uses two digits only, we redirect the
	# UA to the real target.
	if ($fd.did != "" && uri =~ "sip:[0-9][0-9]@") {
		if (sd_lookup("speed_dial")) {
			sl_reply("302", "Speed Dial Redirect");
		}
		else {
			sl_reply("404", "Speed Dial Not Found");
		}
		drop;
	}
}


# Process requests targeted to a local user.
#
route[INBOUND]
{
	# lets see if know the callee
	if (!lookup_user("$tu.uid", "@ruri")) {
		break;
	}

	# Load the attributes of the callee.
	load_attrs("$tu", "$t.uid");

	# You can check if the called URI is in fact an alias like this.
	#if (! $tu.uri_canonical) {
	#	# If the alias URI has different attributes, you can load
	#	# them into the URI track like this.
	#	load_attrs("$tr", "@ruri");
	#}

	# Check for call forwarding of the callee.
	# Note: The forwarding target has to be full routable URI
	#       in this example.
	if (defined $tu.fwd_always_target && $tu.fwd_always_target != "") {
		attr2uri("$tu.fwd_always_target");

		# If we are forwarding to ourselves, don't remove
		# credentials. Otherwise the request would be challenged
		# again.
		# Note: This doesn't apply to failure_route which may
		# still be problematic -- credentials are already
		# removed when we forward. Consider using a 3xx.
		lookup_domain("$td", "@ruri.host");
		if (defined $t.did && $t.did != "") {
			setflag(FLAG_DONT_RM_CRED);
		}
		route(FORWARD);
	}

	# Native SIP destinations are handled using the usrloc database.
	if (lookup_contacts("location")) {
		append_hf("P-hint: usrloc applied\r\n");

		# We set the tm module timers according to the prefences
		# of the callee (avoid too long ringing of his phones).
		# Note1: Timer values have to be in ms now!
		# Note2: This makes even more sense if you switch to a
		#        voicemail from the FAILURE_ROUTE below.
		if ($t.fr_inv_timer) {
			if ($t.fr_timer) {
				t_set_fr("$t.fr_inv_timer", "$t.fr_timer");
			}
			else {
				t_set_fr("$t.fr_inv_timer");
			}
		}

		# This enables session timer support as long as one side
		# supports it.  If you want to have session timmer support
		# only for calls from your PSTN gateway but not between pure
		# VoIP calls you can remove the comment marks from the if
		# clause in the next line and closing bracket below.
		# WARNING: If at all you should trust IP addresses only in
		#          your local network!
		#if (@src.ip == $gw_ip) {
			route(SESSION_TIMER);
		#}

		route(FORWARD);
	}
	else {
		sl_reply("480", "Temporarily unavailable");
		drop;
	}
}


# Process calls for PSTN.
#
route[PSTN]
{
	# Check some conditions first:
	# PSTN is available for our own users only.
	if (strempty($f.did)) {
		break;
	}

	# If the attribute $gw_ip isn't set, there is no PSTN service
	# active.
	if (!defined $gw_ip) {
		break;
	}

	# And finally, the username of the Request-URI must look like
	# a phone number.
	if (!uri =~ "^sips?:\+?[0-9]{3,18}@") {
		break;
	}

	# You may have to convert the number in the Request-URI into a
	# format that is accepted by your gateway here.

	# Check permissions of the caller for initial INVITEs.
	if (method == "INVITE" && strempty(@to.tag)) {
		if ($f.gw_acl != "1") {
			sl_reply("403", "PSTN Not Permitted");
			drop;
		}
	}

	# If the attribute $asserted_id is set, we add its contents as a
	# Remote-Party-ID header.
	# Depending on your gateway, you may have to add a
	# P-Asserted-Identity header here instead.
	if (defined $asserted_id) {
		xlset_attr("$rpidheader",
			"<sip:%$asserted_id@%@ruri.host>;screen=yes");
		replace_attr_hf("Remote-Party-ID", "$rpidheader");
	}

	# Enable Session Timer support with the gateway.
	route(SESSION_TIMER);

	# Replace the domain part of the Request-URI with the value from
	# the attribute  and send it out.
	attr2uri("$gw_ip", "domain");

	# Set the PSTN_ALLOWED flag. This will be checked on the
	# onsend_route.
	setflag(FLAG_PSTN_ALLOWED);
	route(FORWARD);
}


# Try to process CANCEL requests quickly.
#
route[CATCH_CANCEL]
{
	if (method == CANCEL) {
		# t_relay_cancel() will stop processing if a matching
		# INVITE was found.
		if (!t_relay_cancel()) {
			# An INVITE was found but some error occurred.
			sl_reply("500", "Internal Server Error");
			drop;
		}
		# Bad luck, no corresponding INVITE was found, we have to
		# continue with the script.
	}
}


# Site specific policy.
#
route[SITE_SPECIFIC]
{
	# This is only relevant for requests for one of our domains.
	if (strempty($t.did)) {
		break;
	}

	# Do site specific routing such as peering.
	# For example:
	if (uri=~"^sip:000777") {
		rewritehostport("sems01.iptel.org:5074");
		route(FORWARD);
	}
}

# Process Session-Timer.
#
route[SESSION_TIMER]
{
	# We are only interested in session establishment or session
	# refreshing.
	#
	if (method != "INVITE" && method != "UPDATE") {
		break;
	}

	# Let's check if the Session-Expires header is already present.
	if (@hf_value.session_expires != "") {
		# Compare the Session-Expires header value with the
		# configured Min-SE.
		eval_push("x:%@hf_value.session_expires.uri");
		eval_oper("(int)", -1);
		eval_push("x:%@cfg_get.session_timer.min_se");
		eval_oper("(int)", -1);
		eval_oper(">=", -2);

		# Let's check for the Suported header.
		if (hf_value_exists("Supported", "timer")) {
			# The UAC supports Session-Timer, so we
			# only need to take a look at the values
			if (@eval.pop[-1] == "0") {
				# Session interval is lower than the
				# configured Min-SE
				append_to_reply("Min-SE: %@cfg_get.session_timer.min_se\r\n");
				sl_reply("422", "Session Interval Too Small");
				drop;
			}

			# We store the session expires value for the reply
			# route and mark the attribute for inserting as
			# Record-Route cookie.
			$stimer = @hf_value.session_expires.uri;
			setavpflag($stimer, "dialog_cookie");

			# Set the session timer flag that indicates the
			# UAC supports the extension.
			setflag(FLAG_SESSIONTIMER);
		}
		else {
			# Session epxires was already inserted by some other
			# proxy
			if (@eval.pop[-1] == "0") {
				# Session interval is lower than the
				# configured Min-SE. There is no point in
				# sending 422 response, because the UAC
				# does not support the extension, the values
				# can be corrected instead.
				assign_hf_value("Session-Expires",
					"%@cfg_get.session_timer.min_se");
				remove_hf_value("Min-SE");
				append_hf_value("Min-SE",
					"%@cfg_get.session_timer.min_se");
			}
		}
	}
	else {
		# No Session Timer is requested yet, neither by UAC nor by
		# proxy
		if (@cfg_get.session_timer.default != "0") {
			# Add a Session Expires header to see if the UAS
			# supports Session Timer. We do not insert a
			# Required header because then the call might fail.
			append_hf_value("Session-Expires",
				"%@cfg_get.session_timer.default");
			if (@cfg_get.session_timer.min_se != "90") {
				append_hf_value("Min-SE",
					"%@cfg_get.session_timer.min_se");
			}

			# Mark the attribute to be inserted as a
			# Record-Route cookie
			$stimer = @cfg_get.session_timer.default;
			setavpflag($stimer, "dialog_cookie");
		}
	}
}

# Route which checks and performs ENUM queries
# #
route[ENUM]
{
	# perform ENUM query only if the RURI contains an E.164
	# number as uer part
	if (uri =~ "sip:\+[0-9]?@") {
		# if the ENUM query was successful send it right
		# away of to the new target, otherwise do nothing
		if (enum_query()) {
			route(FORWARD);
		}
	}
}


# Failure route for initial INVITEs.
#
failure_route[FAILURE_ROUTE]
{
	# Mark that we are operating from a failure route.
	setflag(FLAG_FAILUREROUTE);

	if (t_check_status("486|600")) {
		# If we received a busy and a busy target is set, forward
		# it there.
		# Note: Again, the forwarding target has to be a routeable
		# URI. We redirect using 3xx to avoid possible issues with
		# credentials (if we consumed them, they may be missing in
		# a loop, if we don't consume them, messages are bigger and
		# more vulnerable)
		if ($tu.fwd_busy_target != "") {
			#attr2uri("$tu.fwd_busy_target");
			#route(FORWARD);
			attr_destination("$tu.fwd_busy_target");
			t_reply("302", "Redirect On Busy");
		}
		# Alternatively, you could forward the request to
		# SEMS/voicemail here
	}
	else if (t_check_status("408|480")) {
		# If we received no answer and the noanswer target is set,
		# forward it there.
		# Note: See above.
		if ($tu.fwd_noanswer_target != "") {
			#attr2uri("$tu.fwd_noanswer_target");
			#route(FORWARD);
			attr_destination("$tu.fwd_noanswer_target");
			t_reply("302", "Redirect On No Answer");
		}
	}
}


# Onreply route that fixes NAT in responses.
#
onreply_route[REPLY_ROUTE]
{
	# Check and fix the Contact in the reply to
	# allow proper routing of in-dialog messages.
	route(UAS_NAT_DETECTION);

	# If RTP proxy was activated and this is a 18x or 2xx reply with a
	# body, inform RTP proxy.
	if (isflagset(FLAG_RTP_PROXY)
	    && status=~"(18[03])|(2[0-9][0-9])"
	    && @msg.body != "")
	{
		force_rtp_proxy('r');
	}

	# Let's check for session timer support.
	if (isflagset(FLAG_SESSIONTIMER) && status =~ "2[0-9][0-9]") {
		# The UAC wanted to have a session timer.
		if (strempty(@hf_value.session_expires)) {
			# But the UAS does not support it, so we will try
			# to convince the UAC to do it.
			append_hf_value("Session-Expires",
				"%$stimer;refresher=uac");
			if (!hf_value_exists("Require", "timer")) {
				include_hf_value("Require", "timer");
			}
		}
	}
}


# Do some final checks before a request is sent out.
onsend_route
{
	# Bypass check: Eliminate requests to the PSTN gateway if they have
	# not passed ACL checks and are not marked with FLAG_PSTN_ALLOWED
	# but are dialog-initiating requests (no to-tag, no CANCEL, no ACK).
	# This helps to stop policy bypasses (gateway IP uploaded as a
	# forked contact, or a call-forwarding destination, or a DNS name,
	# or a preloaded route, or something else possibly)
	if (defined $g.gw_ip && to_ip==$g.gw_ip && !isflagset(FLAG_PSTN_ALLOWED)
	    && !isflagset(FLAG_TOTAG)
	    && method != "ACK" && method != "CANCEL")
	{
		log(1, "ALERT: non authorized packet for PSTN, dropping...\n%mb\n");

		# You can't use advanced features from onsend_route.
		# xlog("L_ALERT", "non authorized packet for PSTN, dropping...\n%mb\n");
		drop;
	}

	# RFC 1918 relay protection: Useful if SER is attached to an
	# administrative network using private IP address space and you
	# wish to prevent UACs from relaying their packets there.
	#
	# You will have to comment this out, if you are regularly serving
	# an RFC 1918 address space.
	if (to_ip==10.0.0.0/8 || to_ip==172.16.0.0/12
	    || to_ip==192.168.0.0/16)
	{
		log(1, "ALERT: Packet targeted to an RFC1918 address dropped\n");
		drop;
	}
}


# Run every minute by the timer module.
#
route[ON_1MIN_TIMER] {
	# Cleanup expired location records
	# MySQL version:
	db_query("delete from location where expires<utc_timestamp()");
	# PostgreSQL version:
	#db_query("delete from location where expires<now()");


	# Reload domains if domain table has been changed recently.
	# Note: because global attributes are read-only and we can't
	# easily remember the "last" status, we check for changed
	# timestamp in 2 minute time-interval.
	# MySQL version:
	db_query("select value from global_attrs where name='domain_data_version' and type=0 and cast(value as unsigned int) between unix_timestamp(now())-120 and unix_timestamp(now())", "reload");
	# PostgreSQL version:
	#db_query("select value from global_attrs where name='domain_data_version' and type=0 and cast(value as integer) between date_part('epoch', now() - interval '120 seconds') and date_part('epoch', now())", "reload");
	if (@db.fetch.reload.count=="1") {
		# Domain reload only available as fifo command.
		exec_msg("sercmd domain.reload");
	}
	db_close("reload");

	# Reload global attributes (they are cached in memory) if the contents of
	# the global_attrs table has been changed recently.
	db_query("select value from global_attrs where name='gattr_timestamp' and type=0 and cast(value as unsigned int) between unix_timestamp(now())-120 and unix_timestamp(now())", "gattr_reload");
	if (@db.fetch.gattr_reload.count=="1") {
	   exec_msg("sercmd global.reload");
	}
	db_close("gattr_reload");
}