diff --git a/net/siproxd/Makefile b/net/siproxd/Makefile
new file mode 100644
index 000000000..8f93e88bd
--- /dev/null
+++ b/net/siproxd/Makefile
@@ -0,0 +1,8 @@
+PLUGIN_NAME= siproxd
+PLUGIN_VERSION= 0.1
+PLUGIN_COMMENT= Siproxd is a proxy daemon for the SIP protocol
+PLUGIN_DEPENDS= siproxd
+PLUGIN_MAINTAINER= m.muenz@gmail.com
+PLUGIN_DEVEL= yes
+
+.include "../../Mk/plugins.mk"
diff --git a/net/siproxd/pkg-descr b/net/siproxd/pkg-descr
new file mode 100644
index 000000000..7678e3ca3
--- /dev/null
+++ b/net/siproxd/pkg-descr
@@ -0,0 +1,10 @@
+Siproxd is a proxy/masquerading daemon for the SIP protocol.
+It handles registrations of SIP clients on a private IP network
+and performs rewriting of the SIP message bodies to make SIP
+connections work via an masquerading firewall (NAT).
+It allows SIP software clients (like kphone, linphone) or
+SIP hardware clients (Voice over IP phones which are
+SIP-compatible, such as those from Cisco, Grandstream or Snom)
+to work behind an IP masquerading firewall or NAT router.
+
+WWW: http://siproxd.sourceforge.net/
diff --git a/net/siproxd/src/etc/inc/plugins.inc.d/siproxd.inc b/net/siproxd/src/etc/inc/plugins.inc.d/siproxd.inc
new file mode 100644
index 000000000..12008e242
--- /dev/null
+++ b/net/siproxd/src/etc/inc/plugins.inc.d/siproxd.inc
@@ -0,0 +1,49 @@
+ gettext('Siproxd Daemon'),
+ 'configd' => array(
+ 'restart' => array('siproxd restart'),
+ 'start' => array('siproxd start'),
+ 'stop' => array('siproxd stop'),
+ ),
+ 'name' => 'siproxd',
+ 'pidfile' => '/var/run/siproxd/siproxd.pid'
+ );
+ }
+
+ return $services;
+}
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/DomainController.php b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/DomainController.php
new file mode 100644
index 000000000..880890f2e
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/DomainController.php
@@ -0,0 +1,205 @@
+request->isGet()) {
+ $mdlDomain = new Domain();
+ $result['domain'] = $mdlDomain->getNodes();
+ }
+ return $result;
+ }
+
+ public function setAction()
+ {
+ $result = array("result"=>"failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlDomain = new Domain();
+ $mdlDomain->setNodes($this->request->getPost("domain"));
+ // perform validation
+ $valMsgs = $mdlDomain->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["domain.".$msg->getField()] = $msg->getMessage();
+ }
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlDomain->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+
+ public function searchDomainAction()
+ {
+ $this->sessionClose();
+ $mdlDomain = $this->getModel();
+ $grid = new UIModelGrid($mdlDomain->domains->domain);
+ return $grid->fetchBindRequest(
+ $this->request,
+ array("enabled", "name", "host", "port" )
+ );
+ }
+
+ public function getDomainAction($uuid = null)
+ {
+ $mdlDomain = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlDomain->getNodeByReference('domains.domain.' . $uuid);
+ if ($node != null) {
+ // return node
+ return array("domain" => $node->getNodes());
+ }
+ } else {
+ $node = $mdlDomain->domains->domain->add();
+ return array("domain" => $node->getNodes());
+ }
+ return array();
+ }
+
+ public function addDomainAction()
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost() && $this->request->hasPost("domain")) {
+ $result = array("result" => "failed", "validations" => array());
+ $mdlDomain = $this->getModel();
+ $node = $mdlDomain->domains->domain->Add();
+ $node->setNodes($this->request->getPost("domain"));
+ $valMsgs = $mdlDomain->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "domain", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (count($result['validations']) == 0) {
+ unset($result['validations']);
+ // save config if validated correctly
+ $mdlDomain->serializeToConfig();
+ Config::getInstance()->save();
+ unset($result['validations']);
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+
+ public function delDomainAction($uuid)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlDomain = $this->getModel();
+ if ($uuid != null) {
+ if ($mdlDomain->domains->domain->del($uuid)) {
+ $mdlDomain->serializeToConfig();
+ Config::getInstance()->save();
+ $result['result'] = 'deleted';
+ } else {
+ $result['result'] = 'not found';
+ }
+ }
+ }
+ return $result;
+ }
+
+ public function setDomainAction($uuid)
+ {
+ if ($this->request->isPost() && $this->request->hasPost("domain")) {
+ $mdlSetting = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlSetting->getNodeByReference('domains.domain.' . $uuid);
+ if ($node != null) {
+ $result = array("result" => "failed", "validations" => array());
+ $domainInfo = $this->request->getPost("domain");
+ $node->setNodes($domainInfo);
+ $valMsgs = $mdlSetting->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "domain", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (count($result['validations']) == 0) {
+ // save config if validated correctly
+ $mdlSetting->serializeToConfig();
+ Config::getInstance()->save();
+ $result = array("result" => "saved");
+ }
+ return $result;
+ }
+ }
+ }
+ return array("result" => "failed");
+ }
+
+ public function toggle_handler($uuid, $elements, $element)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlSetting = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlSetting->getNodeByReference($elements . '.'. $element .'.' . $uuid);
+ if ($node != null) {
+ if ($node->enabled->__toString() == "1") {
+ $result['result'] = "Disabled";
+ $node->enabled = "0";
+ } else {
+ $result['result'] = "Enabled";
+ $node->enabled = "1";
+ }
+ // if item has toggled, serialize to config and save
+ $mdlSetting->serializeToConfig();
+ Config::getInstance()->save();
+ }
+ }
+ }
+ return $result;
+ }
+
+ public function toggleDomainAction($uuid)
+ {
+ return $this->toggle_handler($uuid, 'domains', 'domain');
+ }
+}
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/GeneralController.php b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/GeneralController.php
new file mode 100644
index 000000000..4554375ea
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/GeneralController.php
@@ -0,0 +1,77 @@
+request->isGet()) {
+ $mdlGeneral = new General();
+ $result['general'] = $mdlGeneral->getNodes();
+ }
+ return $result;
+ }
+
+ public function setAction()
+ {
+ $result = array("result"=>"failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlGeneral = new General();
+ $mdlGeneral->setNodes($this->request->getPost("general"));
+
+ // perform validation
+ $valMsgs = $mdlGeneral->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["general.".$msg->getField()] = $msg->getMessage();
+ }
+
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlGeneral->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+}
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/ServiceController.php b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/ServiceController.php
new file mode 100644
index 000000000..b55220e02
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/ServiceController.php
@@ -0,0 +1,159 @@
+configdRun("siproxd show-registrations");
+ return array("response" => $response);
+ }
+
+ /**
+ * start siproxd service (in background)
+ * @return array
+ */
+ public function startAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun("siproxd start", true);
+ return array("response" => $response);
+ } else {
+ return array("response" => array());
+ }
+ }
+
+ /**
+ * stop siproxd service
+ * @return array
+ */
+ public function stopAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun("siproxd stop");
+ return array("response" => $response);
+ } else {
+ return array("response" => array());
+ }
+ }
+
+ /**
+ * restart siproxd service
+ * @return array
+ */
+ public function restartAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun("siproxd restart");
+ return array("response" => $response);
+ } else {
+ return array("response" => array());
+ }
+ }
+
+ /**
+ * retrieve status of siproxd
+ * @return array
+ * @throws \Exception
+ */
+ public function statusAction()
+ {
+ $backend = new Backend();
+ $mdlGeneral = new General();
+ $response = $backend->configdRun("siproxd status");
+
+ if (strpos($response, "not running") > 0) {
+ if ($mdlGeneral->enabled->__toString() == 1) {
+ $status = "stopped";
+ } else {
+ $status = "disabled";
+ }
+ } elseif (strpos($response, "is running") > 0) {
+ $status = "running";
+ } elseif ($mdlGeneral->enabled->__toString() == 0) {
+ $status = "disabled";
+ } else {
+ $status = "unkown";
+ }
+
+
+ return array("status" => $status);
+ }
+
+ /**
+ * reconfigure siproxd, generate config and reload
+ */
+ public function reconfigureAction()
+ {
+ if ($this->request->isPost()) {
+ // close session for long running action
+ $this->sessionClose();
+
+ $mdlGeneral = new General();
+ $backend = new Backend();
+
+ $runStatus = $this->statusAction();
+
+ // stop siproxd if it is running or not
+ $this->stopAction();
+
+ // generate template
+ $backend->configdRun('template reload OPNsense/Siproxd');
+
+ // (re)start daemon
+ if ($mdlGeneral->enabled->__toString() == 1) {
+ $this->startAction();
+ }
+
+ return array("status" => "ok");
+ } else {
+ return array("status" => "failed");
+ }
+ }
+}
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/UserController.php b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/UserController.php
new file mode 100644
index 000000000..c3b0a6699
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/Api/UserController.php
@@ -0,0 +1,205 @@
+request->isGet()) {
+ $mdlUser = new User();
+ $result['user'] = $mdlUser->getNodes();
+ }
+ return $result;
+ }
+
+ public function setAction()
+ {
+ $result = array("result"=>"failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlUser = new User();
+ $mdlUser->setNodes($this->request->getPost("user"));
+ // perform validation
+ $valMsgs = $mdlUser->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["user.".$msg->getField()] = $msg->getMessage();
+ }
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlUser->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+
+ public function searchUserAction()
+ {
+ $this->sessionClose();
+ $mdlUser = $this->getModel();
+ $grid = new UIModelGrid($mdlUser->users->user);
+ return $grid->fetchBindRequest(
+ $this->request,
+ array("enabled", "username", "password" )
+ );
+ }
+
+ public function getUserAction($uuid = null)
+ {
+ $mdlUser = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlUser->getNodeByReference('users.user.' . $uuid);
+ if ($node != null) {
+ // return node
+ return array("user" => $node->getNodes());
+ }
+ } else {
+ $node = $mdlUser->users->user->add();
+ return array("user" => $node->getNodes());
+ }
+ return array();
+ }
+
+ public function addUserAction()
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost() && $this->request->hasPost("user")) {
+ $result = array("result" => "failed", "validations" => array());
+ $mdlUser = $this->getModel();
+ $node = $mdlUser->users->user->Add();
+ $node->setNodes($this->request->getPost("user"));
+ $valMsgs = $mdlUser->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "user", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (count($result['validations']) == 0) {
+ unset($result['validations']);
+ // save config if validated correctly
+ $mdlUser->serializeToConfig();
+ Config::getInstance()->save();
+ unset($result['validations']);
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+
+ public function delUserAction($uuid)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlUser = $this->getModel();
+ if ($uuid != null) {
+ if ($mdlUser->users->user->del($uuid)) {
+ $mdlUser->serializeToConfig();
+ Config::getInstance()->save();
+ $result['result'] = 'deleted';
+ } else {
+ $result['result'] = 'not found';
+ }
+ }
+ }
+ return $result;
+ }
+
+ public function setUserAction($uuid)
+ {
+ if ($this->request->isPost() && $this->request->hasPost("user")) {
+ $mdlSetting = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlSetting->getNodeByReference('users.user.' . $uuid);
+ if ($node != null) {
+ $result = array("result" => "failed", "validations" => array());
+ $userInfo = $this->request->getPost("user");
+ $node->setNodes($userInfo);
+ $valMsgs = $mdlSetting->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "user", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (count($result['validations']) == 0) {
+ // save config if validated correctly
+ $mdlSetting->serializeToConfig();
+ Config::getInstance()->save();
+ $result = array("result" => "saved");
+ }
+ return $result;
+ }
+ }
+ }
+ return array("result" => "failed");
+ }
+
+ public function toggle_handler($uuid, $elements, $element)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlSetting = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlSetting->getNodeByReference($elements . '.'. $element .'.' . $uuid);
+ if ($node != null) {
+ if ($node->enabled->__toString() == "1") {
+ $result['result'] = "Disabled";
+ $node->enabled = "0";
+ } else {
+ $result['result'] = "Enabled";
+ $node->enabled = "1";
+ }
+ // if item has toggled, serialize to config and save
+ $mdlSetting->serializeToConfig();
+ Config::getInstance()->save();
+ }
+ }
+ }
+ return $result;
+ }
+
+ public function toggleUserAction($uuid)
+ {
+ return $this->toggle_handler($uuid, 'users', 'user');
+ }
+}
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/GeneralController.php b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/GeneralController.php
new file mode 100644
index 000000000..d3de941e0
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/GeneralController.php
@@ -0,0 +1,41 @@
+view->title = gettext("Siproxd Settings");
+ $this->view->generalForm = $this->getForm("general");
+ $this->view->formDialogEditSiproxdUser = $this->getForm("dialogEditSiproxdUser");
+ $this->view->formDialogEditSiproxdDomain = $this->getForm("dialogEditSiproxdDomain");
+ $this->view->pick('OPNsense/Siproxd/general');
+ }
+}
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/dialogEditSiproxdDomain.xml b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/dialogEditSiproxdDomain.xml
new file mode 100644
index 000000000..d75374313
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/dialogEditSiproxdDomain.xml
@@ -0,0 +1,26 @@
+
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/dialogEditSiproxdUser.xml b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/dialogEditSiproxdUser.xml
new file mode 100644
index 000000000..8d9c0580d
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/dialogEditSiproxdUser.xml
@@ -0,0 +1,20 @@
+
diff --git a/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/general.xml b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/general.xml
new file mode 100644
index 000000000..b0c537ce3
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/controllers/OPNsense/Siproxd/forms/general.xml
@@ -0,0 +1,199 @@
+
diff --git a/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/ACL/ACL.xml b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/ACL/ACL.xml
new file mode 100644
index 000000000..043013c9a
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/ACL/ACL.xml
@@ -0,0 +1,9 @@
+
+
+ Services: Siproxd
+
+ ui/siproxd/*
+ api/siproxd/*
+
+
+
diff --git a/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/Domain.php b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/Domain.php
new file mode 100644
index 000000000..9b8fc0974
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/Domain.php
@@ -0,0 +1,31 @@
+
+ //OPNsense/siproxd/domain
+ Siproxd outbound domain configuration
+ 1.0.0
+
+
+
+
+ 1
+ Y
+
+
+
+ Y
+
+
+
+ Y
+
+
+ 5060
+ Y
+
+
+
+
+
diff --git a/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/General.php b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/General.php
new file mode 100644
index 000000000..eb717394b
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/General.php
@@ -0,0 +1,35 @@
+
+ //OPNsense/siproxd/general
+ Siproxd configuration
+ 1.0.0
+
+
+ 0
+ Y
+
+
+
+ Y
+
+
+
+ Y
+
+
+
+ N
+
+
+
+ N
+ Y
+ ,
+ N
+
+
+
+ N
+ Y
+ ,
+ N
+
+
+
+ N
+ Y
+ ,
+ N
+
+
+ 5060
+ Y
+ 1
+ 65535
+
+
+ 7070
+ Y
+ 1
+ 65535
+
+
+ 7089
+ Y
+ 1
+ 65535
+
+
+ 300
+ Y
+ 1
+ 10000
+
+
+ 46
+ Y
+ 0
+ 64
+
+
+ 0
+ Y
+ 0
+ 64
+
+
+ 0
+ N
+
+
+ 0
+ N
+
+
+ 600
+ Y
+ 1
+ 10000
+
+
+ 500
+ Y
+ 1
+ 10000
+
+
+ 20
+ Y
+ 0
+ 10000
+
+
+
+ N
+
+
+ Option1
+ N
+ Y
+
+ 0 - do not add ;rport to via header
+ 1 - do add ;rport to INCOMING via header only
+ 2 - do add ;rport to OUTGOING via header only
+ 3 - do add ;rport to OUTGOING and INCOMING via headers
+
+
+
+ 0
+ N
+
+
+ 0
+ N
+
+
+
+ N
+
+
+ 1
+ N
+
+
+ 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+ N
+ Y
+ ,
+ N
+
+
+ 1
+ N
+
+
+ 217.0.23.100/32
+ N
+ Y
+ ,
+ N
+
+
+ 1
+ N
+
+
+ 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+ N
+ Y
+ ,
+ N
+
+
+ 0
+ N
+
+
+
+ N
+
+
+
+ N
+
+
+
+ N
+
+
+
diff --git a/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/Menu/Menu.xml b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/Menu/Menu.xml
new file mode 100644
index 000000000..22fc7f01b
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/Menu/Menu.xml
@@ -0,0 +1,5 @@
+
diff --git a/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/User.php b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/User.php
new file mode 100644
index 000000000..c5822fbb7
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/models/OPNsense/Siproxd/User.php
@@ -0,0 +1,31 @@
+
+ //OPNsense/siproxd/user
+ Siproxd user configuration
+ 1.0.0
+
+
+
+
+ 1
+ Y
+
+
+
+ Y
+ /^([0-9a-zA-Z._\-]){1,128}$/u
+
+
+
+ Y
+ /^([0-9a-zA-Z._\-\!\$\%\/\(\)\+\#\=]){1,128}$/u
+
+
+
+
+
diff --git a/net/siproxd/src/opnsense/mvc/app/views/OPNsense/Siproxd/general.volt b/net/siproxd/src/opnsense/mvc/app/views/OPNsense/Siproxd/general.volt
new file mode 100644
index 000000000..08b2354c2
--- /dev/null
+++ b/net/siproxd/src/opnsense/mvc/app/views/OPNsense/Siproxd/general.volt
@@ -0,0 +1,147 @@
+{#
+
+OPNsense® is Copyright © 2014 – 2017 by Deciso B.V.
+This file is Copyright © 2017 by Michael Muenz
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “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
+AUTHOR 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.
+
+#}
+
+
+
+
+
+
+ {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}}
+
+
+
+
+
+
+
+
+
+
+ | {{ lang._('Enabled') }} |
+ {{ lang._('Username') }} |
+ {{ lang._('Password') }} |
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+ | {{ lang._('Enabled') }} |
+ {{ lang._('Name') }} |
+ {{ lang._('Host') }} |
+ {{ lang._('Port') }} |
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+{{ partial("layout_partials/base_dialog",['fields':formDialogEditSiproxdUser,'id':'dialogEditSiproxdUser','label':lang._('Edit User')])}}
+{{ partial("layout_partials/base_dialog",['fields':formDialogEditSiproxdDomain,'id':'dialogEditSiproxdDomain','label':lang._('Edit Outbound Domain')])}}
+
+
diff --git a/net/siproxd/src/opnsense/scripts/OPNsense/Siproxd/setup.sh b/net/siproxd/src/opnsense/scripts/OPNsense/Siproxd/setup.sh
new file mode 100644
index 000000000..faa57758e
--- /dev/null
+++ b/net/siproxd/src/opnsense/scripts/OPNsense/Siproxd/setup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+mkdir -p /var/run/siproxd
+chown -R nobody:nogroup /var/run/siproxd
+chmod 750 /var/run/siproxd
+
+mkdir -p /var/lib/siproxd
+chown -R nobody:nogroup /var/lib/siproxd
+chmod 750 /var/lib/siproxd
diff --git a/net/siproxd/src/opnsense/service/conf/actions.d/actions_siproxd.conf b/net/siproxd/src/opnsense/service/conf/actions.d/actions_siproxd.conf
new file mode 100644
index 000000000..53490bafc
--- /dev/null
+++ b/net/siproxd/src/opnsense/service/conf/actions.d/actions_siproxd.conf
@@ -0,0 +1,35 @@
+[start]
+command:/usr/local/opnsense/scripts/OPNsense/Siproxd/setup.sh;/usr/local/etc/rc.d/siproxd start
+parameters:
+type:script
+message:starting Siproxd
+
+[stop]
+command:/usr/local/etc/rc.d/siproxd stop; exit 0
+parameters:
+type:script
+message:stopping Siproxd
+
+[restart]
+command:/usr/local/opnsense/scripts/OPNsense/Siproxd/setup.sh;/usr/local/etc/rc.d/siproxd restart
+parameters:
+type:script
+message:restarting Siproxd
+
+[reconfigure]
+command:/usr/local/opnsense/scripts/OPNsense/Siproxd/setup.sh;/usr/local/etc/rc.d/siproxd restart
+parameters:
+type:script
+message:reconfigure Siproxd
+
+[status]
+command:/usr/local/etc/rc.d/siproxd status;exit 0
+parameters:
+type:script_output
+message:request Siproxd status
+
+[show-registrations]
+command:/bin/cat /var/lib/siproxd/siproxd_registrations
+parameters:
+type:script_output
+message: Show registered devices
diff --git a/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/+TARGETS b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/+TARGETS
new file mode 100644
index 000000000..9f7bc24fe
--- /dev/null
+++ b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/+TARGETS
@@ -0,0 +1,3 @@
+siproxd:/etc/rc.conf.d/siproxd
+siproxd.conf:/usr/local/etc/siproxd.conf
+siproxd_passwd.cfg:/usr/local/etc/siproxd_passwd.cfg
diff --git a/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd
new file mode 100644
index 000000000..b04b5e856
--- /dev/null
+++ b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd
@@ -0,0 +1,6 @@
+{% if helpers.exists('OPNsense.siproxd.general.enabled') and OPNsense.siproxd.general.enabled == '1' %}
+siproxd_opnsense_bootup_run="/usr/local/opnsense/scripts/OPNsense/Siproxd/setup.sh"
+siproxd_enable="YES"
+{% else %}
+siproxd_enable="NO"
+{% endif %}
diff --git a/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd.conf b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd.conf
new file mode 100644
index 000000000..11e6ca6a6
--- /dev/null
+++ b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd.conf
@@ -0,0 +1,140 @@
+{% if helpers.exists('OPNsense.siproxd.general.enabled') and OPNsense.siproxd.general.enabled == '1' %}
+{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
+
+{% if helpers.exists('OPNsense.siproxd.general.if_inbound') and OPNsense.siproxd.general.if_inbound != '' %}
+if_inbound = {{ physical_interface('OPNsense.siproxd.general.if_inbound') }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.if_outbound') and OPNsense.siproxd.general.if_outbound != '' %}
+if_outbound = {{ physical_interface('OPNsense.siproxd.general.if_outbound') }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.host_outbound') and OPNsense.siproxd.general.host_outbound != '' %}
+host_outbound = {{ OPNsense.siproxd.general.host_outbound }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.hosts_allow_reg') and OPNsense.siproxd.general.hosts_allow_reg != '' %}
+hosts_allow_reg = {{ OPNsense.siproxd.general.hosts_allow_reg }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.hosts_allow_sip') and OPNsense.siproxd.general.hosts_allow_sip != '' %}
+hosts_allow_sip = {{ OPNsense.siproxd.general.hosts_allow_sip }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.hosts_deny_sip') and OPNsense.siproxd.general.hosts_deny_sip != '' %}
+hosts_deny_sip = {{ OPNsense.siproxd.general.hosts_deny_sip }}
+{% endif %}
+
+#hosts_allow_reg = 192.168.1.8/24
+#hosts_allow_sip = 123.45.0.0/16,123.46.0.0/16
+#hosts_deny_sip = 10.0.0.0/8,11.0.0.0/8
+
+{% if helpers.exists('OPNsense.siproxd.general.sip_listen_port') and OPNsense.siproxd.general.sip_listen_port != '' %}
+sip_listen_port = {{ OPNsense.siproxd.general.sip_listen_port }}
+{% endif %}
+
+daemonize = 1
+silence_log = 3
+registration_file = /var/lib/siproxd/siproxd_registrations
+autosave_registrations = 300
+pid_file = /var/run/siproxd/siproxd.pid
+rtp_proxy_enable = 1
+
+{% if helpers.exists('OPNsense.siproxd.general.rtp_port_low') and OPNsense.siproxd.general.rtp_port_low != '' %}
+rtp_port_low = {{ OPNsense.siproxd.general.rtp_port_low }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.rtp_port_high') and OPNsense.siproxd.general.rtp_port_high != '' %}
+rtp_port_low = {{ OPNsense.siproxd.general.rtp_port_high }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.rtp_timeout') and OPNsense.siproxd.general.rtp_timeout != '' %}
+rtp_timeout = {{ OPNsense.siproxd.general.rtp_timeout }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.rtp_dscp') and OPNsense.siproxd.general.rtp_dscp != '' %}
+rtp_dscp = {{ OPNsense.siproxd.general.rtp_dscp }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.sip_dscp') and OPNsense.siproxd.general.sip_dscp != '' %}
+sip_dscp = {{ OPNsense.siproxd.general.sip_dscp }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.rtp_input_dejitter') and OPNsense.siproxd.general.rtp_input_dejitter != '' %}
+rtp_input_dejitter = {{ OPNsense.siproxd.general.rtp_input_dejitter }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.rtp_output_dejitter') and OPNsense.siproxd.general.rtp_output_dejitter != '' %}
+rtp_output_dejitter = {{ OPNsense.siproxd.general.rtp_output_dejitter }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.tcp_timeout') and OPNsense.siproxd.general.tcp_timeout != '' %}
+tcp_timeout = {{ OPNsense.siproxd.general.tcp_timeout }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.tcp_connect_timeout') and OPNsense.siproxd.general.tcp_connect_timeout != '' %}
+tcp_connect_timeout = {{ OPNsense.siproxd.general.tcp_connect_timeout }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.tcp_keepalive') and OPNsense.siproxd.general.tcp_keepalive != '' %}
+tcp_keepalive = {{ OPNsense.siproxd.general.tcp_keepalive }}
+{% endif %}
+
+debug_level = 0x00000000
+debug_port = 0
+
+{% if helpers.exists('OPNsense.siproxd.general.ua_string') and OPNsense.siproxd.general.ua_string != '' %}
+ua_string = {{ OPNsense.siproxd.general.ua_string }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.use_rport') and OPNsense.siproxd.general.use_rport != '' %}
+use_rport = {{ OPNsense.siproxd.general.use_rport }}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.domain.domains.domain') %}
+{% for domain_list in helpers.toList('OPNsense.siproxd.domain.domains.domain') %}
+{% if domain_list.enabled == '1' %}
+outbound_domain_name = {{ domain_list.name }}
+outbound_domain_host = {{ domain_list.host }}
+outbound_domain_port = {{ domain_list.port }}
+
+{% endif %}
+{% endfor %}
+{% endif %}
+
+plugindir=/usr/local/lib/siproxd/
+
+{% if helpers.exists('OPNsense.siproxd.general.plugin_defaulttarget_enable') and OPNsense.siproxd.general.plugin_defaulttarget_enable == '1' %}
+load_plugin=plugin_defaulttarget.la
+{% if helpers.exists('OPNsense.siproxd.general.plugin_defaulttarget_log') and OPNsense.siproxd.general.plugin_defaulttarget_log != '' %}
+plugin_defaulttarget_log = 1
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.plugin_defaulttarget_target') and OPNsense.siproxd.general.plugin_defaulttarget_target != '' %}
+plugin_defaulttarget_target = {{ OPNsense.siproxd.general.plugin_defaulttarget_target }}
+{% endif %}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.plugin_fix_bogus_via_enable') and OPNsense.siproxd.general.plugin_fix_bogus_via_enable == '1' %}
+load_plugin=plugin_fix_bogus_via.la
+{% if helpers.exists('OPNsense.siproxd.general.plugin_fix_bogus_via_networks') and OPNsense.siproxd.general.plugin_fix_bogus_via_networks != '' %}
+plugin_fix_bogus_via_networks = {{ OPNsense.siproxd.general.plugin_fix_bogus_via_networks }}
+{% endif %}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.plugin_fix_DTAG_enable') and OPNsense.siproxd.general.plugin_fix_DTAG_enable == '1' %}
+load_plugin=plugin_fix_DTAG.la
+{% if helpers.exists('OPNsense.siproxd.general.plugin_fix_DTAG_networks') and OPNsense.siproxd.general.plugin_fix_DTAG_networks != '' %}
+plugin_fix_DTAG_networks = {{ OPNsense.siproxd.general.plugin_fix_DTAG_networks }}
+{% endif %}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.plugin_fbox_anoncall_enable') and OPNsense.siproxd.general.plugin_fbox_anoncall_enable == '1' %}
+load_plugin=plugin_fix_fbox_anoncall.la
+{% if helpers.exists('OPNsense.siproxd.general.plugin_fbox_anoncall_networks') and OPNsense.siproxd.general.plugin_fbox_anoncall_networks != '' %}
+plugin_fbox_anoncall_networks = {{ OPNsense.siproxd.general.plugin_fbox_anoncall_networks }}
+{% endif %}
+{% endif %}
+
+{% if helpers.exists('OPNsense.siproxd.general.plugin_stun_server_enable') and OPNsense.siproxd.general.plugin_stun_server_enable == '1' %}
+load_plugin=plugin_stun.la
+{% if helpers.exists('OPNsense.siproxd.general.plugin_stun_server_host') and OPNsense.siproxd.general.plugin_stun_server_host != '' %}
+plugin_stun_server_host = {{ OPNsense.siproxd.general.plugin_stun_server_host }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.plugin_stun_server_port') and OPNsense.siproxd.general.plugin_stun_server_port != '' %}
+plugin_stun_server_port = {{ OPNsense.siproxd.general.plugin_stun_server_port }}
+{% endif %}
+{% if helpers.exists('OPNsense.siproxd.general.plugin_stun_server_period') and OPNsense.siproxd.general.plugin_stun_server_period != '' %}
+plugin_stun_server_period = {{ OPNsense.siproxd.general.plugin_stun_server_period }}
+{% endif %}
+{% endif %}
+
+
+{% endif %}
diff --git a/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd_passwd.cfg b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd_passwd.cfg
new file mode 100644
index 000000000..d32c0ca2e
--- /dev/null
+++ b/net/siproxd/src/opnsense/service/templates/OPNsense/Siproxd/siproxd_passwd.cfg
@@ -0,0 +1,21 @@
+{% if helpers.exists('OPNsense.siproxd.general.enabled') and OPNsense.siproxd.general.enabled == '1' %}
+
+######################################################################
+#
+# Per user password file for siproxd
+#
+# format is:
+#
+# username and password must not contains white spaces
+#
+######################################################################
+
+{% if helpers.exists('OPNsense.siproxd.user.users.user') %}
+{% for user_list in helpers.toList('OPNsense.siproxd.user.users.user') %}
+{% if user_list.enabled == '1' %}
+{{ user_list.username }} {{ user_list.password }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% endif %}