From e5c4279d1bafffc1a0ca6d24943c195eeb1a5bb1 Mon Sep 17 00:00:00 2001
From: Marek Vavrusa <marek.vavrusa@nic.cz>
Date: Fri, 31 Aug 2012 14:11:44 +0200
Subject: [PATCH] Working remote control configuration and reading from socket.

refs #2035
---
 src/knot/conf/cf-lex.l   |  4 +++
 src/knot/conf/cf-parse.y | 52 +++++++++++++++++++++------
 src/knot/ctl/remote.c    | 77 ++++++++++++++++++++++++++++++++++++++++
 src/knot/ctl/remote.h    | 43 ++++++++++++++++++++++
 src/knot/main.c          | 16 +++++++--
 5 files changed, 179 insertions(+), 13 deletions(-)
 create mode 100644 src/knot/ctl/remote.c
 create mode 100644 src/knot/ctl/remote.h

diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l
index 58d1f4b961..caf07828f1 100644
--- a/src/knot/conf/cf-lex.l
+++ b/src/knot/conf/cf-lex.l
@@ -97,6 +97,10 @@ address         { lval.t = yytext; return ADDRESS; }
 port            { lval.t = yytext; return PORT; }
 via             { lval.t = yytext; return VIA; }
 
+control         { lval.t = yytext; return CONTROL; }
+allow           { lval.t = yytext; return ALLOW; }
+listen-on       { lval.t = yytext; return LISTEN_ON; }
+
 log             { lval.t = yytext; return LOG; }
 
 any { lval.t = yytext; lval.i = LOG_ANY; return LOG_SRC; }
diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y
index 69a5415350..7a54f54207 100644
--- a/src/knot/conf/cf-parse.y
+++ b/src/knot/conf/cf-parse.y
@@ -28,7 +28,7 @@ static conf_log_t *this_log = 0;
 static conf_log_map_t *this_logmap = 0;
 //#define YYERROR_VERBOSE 1
 
-static void conf_start_iface(void *scanner, char* ifname)
+static void conf_init_iface(void *scanner, char* ifname)
 {
    this_iface = malloc(sizeof(conf_iface_t));
    if (this_iface == NULL) {
@@ -38,6 +38,11 @@ static void conf_start_iface(void *scanner, char* ifname)
    memset(this_iface, 0, sizeof(conf_iface_t));
    this_iface->name = ifname;
    this_iface->port = CONFIG_DEFAULT_PORT;
+}
+
+static void conf_start_iface(void *scanner, char* ifname)
+{
+   conf_init_iface(scanner, ifname);
    add_tail(&new_config->ifaces, &this_iface->n);
    ++new_config->ifaces_count;
 }
@@ -49,7 +54,8 @@ static void conf_start_remote(void *scanner, char *remote)
       cf_error(scanner, "not enough memory when allocating remote");
       return;
    }
-   memset(this_remote, 0, sizeof(conf_iface_t));
+   
+memset(this_remote, 0, sizeof(conf_iface_t));
    this_remote->name = remote;
    add_tail(&new_config->remotes, &this_remote->n);
    sockaddr_init(&this_remote->via, -1);
@@ -274,6 +280,8 @@ static int conf_mask(void* scanner, int nval, int prefixlen) {
 %token <tok> IPA6
 %token <tok> VIA
 
+%token <tok> CONTROL ALLOW LISTEN_ON
+
 %token <tok> LOG
 %token <tok> LOG_DEST
 %token <tok> LOG_SRC
@@ -294,10 +302,10 @@ interface_start:
  | LOG_SRC  { conf_start_iface(scanner, strdup($1.t)); }
  | LOG  { conf_start_iface(scanner, strdup($1.t)); }
  | LOG_LEVEL  { conf_start_iface(scanner, strdup($1.t)); }
+ | CONTROL    { conf_start_iface(scanner, strdup($1.t)); }
  ;
 
 interface:
-   interface_start '{'
  | interface PORT NUM ';' {
      if (this_iface->port != CONFIG_DEFAULT_PORT) {
        cf_error(scanner, "only one port definition is allowed in interface section\n");
@@ -351,7 +359,7 @@ interface:
 
 interfaces:
    INTERFACES '{'
- | interfaces interface '}' {
+ | interfaces interface_start '{' interface '}' {
    if (this_iface->address == 0) {
      char buf[512];
      snprintf(buf, sizeof(buf), "interface '%s' has no defined address", this_iface->name);
@@ -463,10 +471,10 @@ remote_start:
  | LOG_SRC  { conf_start_remote(scanner, strdup($1.t)); }
  | LOG  { conf_start_remote(scanner, strdup($1.t)); }
  | LOG_LEVEL  { conf_start_remote(scanner, strdup($1.t)); }
+ | CONTROL    { conf_start_remote(scanner, strdup($1.t)); }
  ;
 
 remote:
-   remote_start '{'
  | remote PORT NUM ';' {
      if (this_remote->port != 0) {
        cf_error(scanner, "only one port definition is allowed in remote section\n");
@@ -562,7 +570,7 @@ remote:
 
 remotes:
    REMOTES '{'
- | remotes remote '}' {
+ | remotes remote_start '{' remote '}' {
      if (this_remote->address == 0) {
        char buf[512];
        snprintf(buf, sizeof(buf), "remote '%s' has no defined address", this_remote->name);
@@ -591,16 +599,15 @@ zone_acl_item:
  | LOG_SRC  { conf_acl_item(scanner, strdup($1.t)); }
  | LOG  { conf_acl_item(scanner, strdup($1.t)); }
  | LOG_LEVEL  { conf_acl_item(scanner, strdup($1.t)); }
+ | CONTROL    { conf_acl_item(scanner, strdup($1.t)); }
  ;
 
 zone_acl_list:
-   zone_acl_start
  | zone_acl_list zone_acl_item ','
  | zone_acl_list zone_acl_item ';'
  ;
 
 zone_acl:
-   zone_acl_start '{'
  | zone_acl TEXT ';' {
       /* Find existing node in remotes. */
       node* r = 0; conf_iface_t* found = 0;
@@ -637,6 +644,7 @@ zone_start:
  | LOG_SRC { conf_zone_start(scanner, strdup($1.t)); }
  | LOG { conf_zone_start(scanner, strdup($1.t)); }
  | LOG_LEVEL { conf_zone_start(scanner, strdup($1.t)); }
+ | CONTROL    { conf_zone_start(scanner, strdup($1.t)); }
  | NUM '/' TEXT {
     if ($1.i < 0 || $1.i > 255) {
         char buf[256] = "";
@@ -661,8 +669,8 @@ zone_start:
 
 zone:
    zone_start '{'
- | zone zone_acl '}'
- | zone zone_acl_list
+ | zone zone_acl_start '{' zone_acl '}'
+ | zone zone_acl_start zone_acl_list
  | zone FILENAME TEXT ';' { this_zone->file = $3.t; }
  | zone BUILD_DIFFS BOOL ';' { this_zone->build_diffs = $3.i; }
  | zone SEMANTIC_CHECKS BOOL ';' { this_zone->enable_checks = $3.i; }
@@ -801,8 +809,30 @@ log_start:
 
 log: LOG '{' log_start log_end;
 
+ctl_listen_start:
+  LISTEN_ON { conf_init_iface(scanner, NULL); }
+  ;
+
+ctl_allow_start:
+  ALLOW {
+    this_list = &new_config->ctl.allow;
+  }
+  ;
+
+control:
+   CONTROL '{'
+ | control ctl_listen_start '{' interface '}' {
+     if (this_iface->address == 0) {
+       cf_error(scanner, "control interface has no defined address");
+     } else {
+       new_config->ctl.iface = this_iface;
+     }
+ }
+ | control ctl_allow_start '{' zone_acl '}'
+ | control ctl_allow_start zone_acl_list
+ ; 
 
-conf: ';' | system '}' | interfaces '}' | keys '}' | remotes '}' | zones '}' | log '}';
+conf: ';' | system '}' | interfaces '}' | keys '}' | remotes '}' | zones '}' | log '}' | control '}';
 
 %%
 
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
new file mode 100644
index 0000000000..e0e2ccf156
--- /dev/null
+++ b/src/knot/ctl/remote.c
@@ -0,0 +1,77 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "remote.h"
+#include "common/log.h"
+#include "common/fdset.h"
+#include "knot/conf/conf.h"
+#include "knot/other/error.h"
+#include "knot/server/socket.h"
+#include "knot/server/tcp-handler.h"
+
+int remote_bind(conf_iface_t *desc)
+{
+	if (desc == NULL) {
+		return -1;
+	}
+	
+	/* Create new socket. */
+	int s = socket_create(desc->family, SOCK_STREAM);
+	if (s < 0) {
+		log_server_error("Couldn't create socket for remote "
+				 "control interface - %s",
+				 knotd_strerror(s));
+		return -1;
+	}
+	
+	/* Bind to interface and start listening. */
+	int r = socket_bind(s, desc->family, desc->address, desc->port);
+	if (r == KNOTD_EOK) {
+		r = socket_listen(s, TCP_BACKLOG_SIZE);
+	}
+	
+	if (r != KNOTD_EOK) {
+		socket_close(s);
+		log_server_error("Could not bind to "
+				 "remote control interface %s port %d.\n",
+				 desc->address, desc->port);
+		return -1;
+	}
+	
+	return s;
+}
+
+int remote_unbind(int r)
+{
+	if (r < 0) {
+		return KNOTD_EINVAL;
+	}
+	
+	return socket_close(r);
+}
+
+int remote_poll(int r)
+{
+	if (r < 0) {
+		return -1;
+	}
+	
+	/* Wait for events. */
+	fd_set rfds;
+	FD_ZERO(&rfds);
+	FD_SET(r, &rfds);
+	return fdset_pselect(r + 1, &rfds, NULL, NULL, NULL, NULL);
+}
diff --git a/src/knot/ctl/remote.h b/src/knot/ctl/remote.h
new file mode 100644
index 0000000000..91c5f776b5
--- /dev/null
+++ b/src/knot/ctl/remote.h
@@ -0,0 +1,43 @@
+/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/*!
+ * \file remote.h
+ *
+ * \author Marek Vavrusa <marek.vavrusa@nic.cz>
+ *
+ * \brief Functions for remote control interface.
+ *
+ * \addtogroup ctl
+ * @{
+ */
+
+#ifndef _KNOTD_REMOTE_H_
+#define _KNOTD_REMOTE_H_
+
+#include "knot/conf/conf.h"
+
+int remote_bind(conf_iface_t *desc);
+int remote_unbind(int r);
+int remote_poll(int r);
+
+// encrypted comm
+// remote_accept (r, key)
+// remote_send (r, key)
+
+
+#endif // _KNOTD_REMOTE_H_
+
+/*! @} */
diff --git a/src/knot/main.c b/src/knot/main.c
index dade8a3283..9fcd4191d3 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -304,7 +304,13 @@ int main(int argc, char **argv)
 		pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
 
 		/* Bind to control interface. */
-		int remote = remote_bind(conf()->ctl.iface);
+		conf_iface_t *ctl_if = conf()->ctl.iface;
+		if (ctl_if != NULL) {
+			log_server_info("Binding remote control interface "
+					"to %s port %d.\n",
+					ctl_if->address, ctl_if->port);
+		}
+		int remote = remote_bind(ctl_if);
 		
 		/* Run event loop. */
 		for(;;) {
@@ -356,6 +362,7 @@ int main(int argc, char **argv)
 
 			/* Events. */
 			if (ret > 0) {
+				fprintf(stderr, "remote: accepting..\n");
 				int c = tcp_accept(remote);
 				if (c < 0) {
 					continue;
@@ -367,18 +374,23 @@ int main(int argc, char **argv)
 				
 				sockaddr_t addr;
 				sockaddr_init(&addr, AF_INET);
-				int r = tcp_recv(c, buf, buflen, &addr);
+				fprintf(stderr, "remote: reading..\n");
+				int r = recv(c, buf, buflen, MSG_WAITALL);
 				if (r < 0) {
+					perror("tcp_recv");
+					fprintf(stderr, "remote: read shit..\n");
 					close(c);
 					continue;
 				}
 				
+				getpeername(c, addr.ptr, &addr.len);
 				char straddr[SOCKADDR_STRLEN];
 				sockaddr_tostr(&addr, straddr, sizeof(straddr));
 				fprintf(stderr, "remote: accepted %d bytes from %s:%d\n",
 					r, straddr, sockaddr_portnum(&addr));
 				
 				close(c);
+				fprintf(stderr, "remote: i'm so done with this\n");
 			}
 		}
 		pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
-- 
GitLab