added config parser & incoming connection handler
[NextIRCd.git] / src / ircd_sock.c
diff --git a/src/ircd_sock.c b/src/ircd_sock.c
new file mode 100644 (file)
index 0000000..91674b4
--- /dev/null
@@ -0,0 +1,214 @@
+/* ircd_sock.c - NextIRCd
+ * Copyright (C) 2012-2013  Philipp Kreil (pk910)
+ * 
+ * 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 "config.h"
+#include "ircd_sock.h"
+#include "ircd_client.h"
+#include "struct_connection.h"
+
+#include "IOHandler/IOSockets.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> // @debug
+
+static struct Connection *sockets_listening = NULL;
+static struct Connection *sockets_first = NULL;
+static struct Connection *sockets_last = NULL;
+
+static CONFRELOAD_CALLBACK(sockets_config_reload);
+static IOSOCKET_CALLBACK(sockets_iohandler_callback);
+static void sockets_free_connection(struct Connection *sock);
+
+void init_sockets() {
+       reload_config_callback(sockets_config_reload);
+       sockets_config_reload(); /* open all listening sockets */
+}
+
+
+static void sockets_append_list(struct Connection *conn) {
+       if(conn->listening) {
+               conn->next = sockets_listening;
+               sockets_listening = conn;
+       } else {
+               conn->next = NULL;
+               conn->prev = sockets_last;
+               if(sockets_last)
+                       sockets_last->next = conn;
+               sockets_last = conn;
+               if(!sockets_first)
+                       sockets_first = conn;
+       }
+}
+
+static void sockets_remove_list(struct Connection *conn) {
+       if(conn->listening) {
+               struct Connection *prev;
+               if(sockets_listening == conn)
+                       sockets_listening = conn->next;
+               else {
+                       for(prev = sockets_listening; prev; prev = prev->next) {
+                               if(prev->next == conn)
+                                       break;
+                       }
+                       if(prev)
+                               prev->next = conn->next;
+               }
+       } else {
+               if(conn->next)
+                       conn->next->prev = conn->prev;
+               else
+                       sockets_last = conn->prev;
+               if(conn->prev)
+                       conn->prev->next = conn->next;
+               else
+                       sockets_first = conn->next;
+       }
+}
+
+static void sockets_free_connection(struct Connection *conn) {
+    free(conn);
+}
+
+static void sockets_close_listening() {
+       struct Connection *conn;
+       while((conn = sockets_listening)) {
+               sockets_remove_list(conn);
+               iosocket_close(conn->socket);
+               sockets_free_connection(conn);
+       }
+}
+
+static int sockets_add_listening(struct ConfigPortObject *conf_port, char *bind_addr, char *certfile, char *keyfile, int socket_flags) {
+    if(!conf_port)
+        return -1;
+    
+    struct IOSocket *iosock;
+    if(!conf_port->secure)
+        iosock = iosocket_listen_flags(bind_addr, conf_port->port, sockets_iohandler_callback, socket_flags);
+    else if(certfile && keyfile)
+        iosock = iosocket_listen_ssl_flags(bind_addr, conf_port->port, certfile, keyfile, sockets_iohandler_callback, socket_flags);
+    else 
+        return -1;
+    
+    if(!iosock)
+        return -1;
+    
+    printf("Added Listener Socket to Port %d.\n", conf_port->port); // @debug
+    
+    struct Connection *connection;
+    connection = calloc(1, sizeof(*connection));
+    connection->listening = 1;
+    connection->socket = iosock;
+    connection->server = conf_port->server;
+    iosock->data = connection;
+    sockets_append_list(connection);
+    return 0;
+}
+
+static void sockets_accept_client(struct IOSocket *new_client, struct Connection *listener) {
+    struct Connection *connection;
+    connection = calloc(1, sizeof(*connection));
+    connection->socket = new_client;
+    connection->ssl = new_client->ssl;
+    
+    new_client->parse_delimiter = 1;
+    new_client->parse_empty = 0;
+    strcpy((char*) new_client->delimiters, "\r\n");
+    
+    connection->parent = listener;
+    new_client->data = connection;
+    sockets_append_list(connection);
+    
+    client_connected(connection);
+}
+
+static CONFRELOAD_CALLBACK(sockets_config_reload) {
+       struct ConfigPortObject *conf_port;
+       char *certfile, *keyfile;
+       
+       sockets_close_listening();
+       
+       certfile = NULL; /* to be continued */
+       keyfile = NULL;
+       
+       for(conf_port = global_config.ports; conf_port; conf_port = conf_port->next) {
+        printf("port: %d\n", conf_port->port); // @debug
+        
+        //add 2 listeners (IPv4 & IPv6) here
+               if(!conf_port->ip4only) {
+            char *bind_addr = conf_port->bind_addr;
+            if(!bind_addr)
+                bind_addr = "::1";
+            sockets_add_listening(conf_port, bind_addr, certfile, keyfile, IOSOCKET_ADDR_IPV6);
+        }
+        
+               if(!conf_port->ip6only) {
+            char *bind_addr = conf_port->bind_addr;
+            if(!bind_addr)
+                bind_addr = "0.0.0.0";
+            sockets_add_listening(conf_port, bind_addr, certfile, keyfile, IOSOCKET_ADDR_IPV4);
+        }
+       }
+}
+
+static IOSOCKET_CALLBACK(sockets_iohandler_callback) {
+       struct Connection *connection = event->socket->data;
+    
+    switch(event->type) {
+    case IOSOCKETEVENT_ACCEPT:
+        sockets_accept_client(event->data.accept_socket, connection);
+        break;
+    
+    case IOSOCKETEVENT_NOTCONNECTED:
+    case IOSOCKETEVENT_CLOSED:
+        sockets_remove_list(connection);
+        if(connection->server) {
+        
+        } else {
+            client_disconnected(connection);
+        }
+        sockets_free_connection(connection);
+        break;
+    
+    case IOSOCKETEVENT_DNSFAILED:
+        if(connection->listening) {
+            // listening socket could not be opened
+        } else {
+            sockets_remove_list(connection);
+            client_disconnected(connection);
+            sockets_free_connection(connection);
+        }
+        break;
+    
+    case IOSOCKETEVENT_RECV:
+        if(connection->server) {
+            
+        } else {
+            client_recv(connection, event->data.recv_str);
+        }
+        break;
+    
+    default:
+        break;
+    }
+}
+
+void socket_send(struct Connection *conn, char *data, int len) {
+    iosocket_send(conn->socket, data, len);
+}
+