]> code.citadel.org Git - citadel.git/blobdiff - citadel/serv_network.c
* Completed migrating the "netpoll" utility into the serv_network module.
[citadel.git] / citadel / serv_network.c
index 30dc7fe378a74a0a1d04d93ac80288d136c50585..d28b717e4999ea4bd35cfb4d5bbd1910202d5a10 100644 (file)
@@ -9,6 +9,13 @@
  *
  */
 
+/*
+ * FIXME do something about concurrency issues:
+ * 1. Don't allow the two nodes to poll each other at the same time
+ * 2. Don't allow polls during network processing
+ * 3. Kill Bill Gates using either a chainsaw or a wood chipper
+ */
+
 #include "sysdep.h"
 #include <stdlib.h>
 #include <unistd.h>
@@ -48,6 +55,7 @@
 #include "tools.h"
 #include "internet_addressing.h"
 #include "serv_network.h"
+#include "clientsocket.h"
 
 
 /*
@@ -144,9 +152,10 @@ void write_network_map(void) {
 /* 
  * Check the network map and determine whether the supplied node name is
  * valid.  If it is not a neighbor node, supply the name of a neighbor node
- * which is the next hop.
+ * which is the next hop.  If it *is* a neighbor node, we also fill in the
+ * shared secret.
  */
-int is_valid_node(char *nexthop, char *node) {
+int is_valid_node(char *nexthop, char *secret, char *node) {
        char *ignetcfg = NULL;
        int i;
        char linebuf[SIZ];
@@ -182,6 +191,9 @@ int is_valid_node(char *nexthop, char *node) {
                        if (nexthop != NULL) {
                                strcpy(nexthop, "");
                        }
+                       if (secret != NULL) {
+                               extract(secret, linebuf, 1);
+                       }
                        retval = 0;
                }
        }
@@ -286,7 +298,7 @@ void network_spool_msg(long msgnum, void *userdata) {
        char *instr = NULL;
        char *newpath = NULL;
        size_t instr_len = SIZ;
-       struct CtdlMessage *msg;
+       struct CtdlMessage *msg = NULL;
        struct CtdlMessage *imsg;
        struct ser_ret sermsg;
        FILE *fp;
@@ -396,7 +408,6 @@ void network_spool_msg(long msgnum, void *userdata) {
                         * Now serialize it for transmission
                         */
                        serialize_message(&sermsg, msg);
-                       CtdlFreeMessage(msg);
 
                        /* Now send it to every node */
                        for (nptr = sc->ignet_push_shares; nptr != NULL;
@@ -405,13 +416,14 @@ void network_spool_msg(long msgnum, void *userdata) {
                                send = 1;
 
                                /* Check for valid node name */
-                               if (is_valid_node(NULL, nptr->name) != 0) {
+                               if (is_valid_node(NULL,NULL,nptr->name) != 0) {
                                        lprintf(3, "Invalid node <%s>\n",
                                                nptr->name);
                                        send = 0;
                                }
 
                                /* Check for split horizon */
+                               lprintf(9, "Path is %s\n", msg->cm_fields['P']);
                                bang = num_tokens(msg->cm_fields['P'], '!');
                                if (bang > 1) for (i=0; i<(bang-1); ++i) {
                                        extract_token(buf, msg->cm_fields['P'],
@@ -435,6 +447,7 @@ void network_spool_msg(long msgnum, void *userdata) {
                                }
                        }
                        phree(sermsg.ser);
+                       CtdlFreeMessage(msg);
                }
        }
 
@@ -631,7 +644,8 @@ void network_process_buffer(char *buffer, long size) {
                if (strcasecmp(msg->cm_fields['D'], config.c_nodename)) {
 
                        /* route the message */
-                       if (is_valid_node(NULL, msg->cm_fields['D']) == 0) {
+                       if (is_valid_node(NULL, NULL,
+                          msg->cm_fields['D']) == 0) {
 
                                /* prepend our node to the path */
                                if (msg->cm_fields['P'] != NULL) {
@@ -808,6 +822,226 @@ void network_do_spoolin(void) {
 }
 
 
+
+
+
+/*
+ * receive network spool from the remote system
+ */
+void receive_spool(int sock, char *remote_nodename) {
+       long download_len;
+       long bytes_received;
+       char buf[SIZ];
+       static char pbuf[IGNET_PACKET_SIZE];
+       char tempfilename[PATH_MAX];
+       long plen;
+       FILE *fp;
+
+       strcpy(tempfilename, tmpnam(NULL));
+       if (sock_puts(sock, "NDOP") < 0) return;
+       if (sock_gets(sock, buf) < 0) return;
+       lprintf(9, "<%s\n", buf);
+       if (buf[0] != '2') {
+               return;
+       }
+       download_len = extract_long(&buf[4], 0);
+
+       bytes_received = 0L;
+       fp = fopen(tempfilename, "w");
+       if (fp == NULL) {
+               lprintf(9, "cannot open download file locally: %s\n",
+                       strerror(errno));
+               return;
+       }
+
+       while (bytes_received < download_len) {
+               sprintf(buf, "READ %ld|%ld",
+                       bytes_received,
+                    ((download_len - bytes_received > IGNET_PACKET_SIZE)
+                ? IGNET_PACKET_SIZE : (download_len - bytes_received)));
+               if (sock_puts(sock, buf) < 0) {
+                       fclose(fp);
+                       unlink(tempfilename);
+                       return;
+               }
+               if (sock_gets(sock, buf) < 0) {
+                       fclose(fp);
+                       unlink(tempfilename);
+                       return;
+               }
+               if (buf[0] == '6') {
+                       plen = extract_long(&buf[4], 0);
+                       if (sock_read(sock, pbuf, plen) < 0) {
+                               fclose(fp);
+                               unlink(tempfilename);
+                               return;
+                       }
+                       fwrite((char *) pbuf, plen, 1, fp);
+                       bytes_received = bytes_received + plen;
+               }
+       }
+
+       fclose(fp);
+       if (sock_puts(sock, "CLOS") < 0) {
+               unlink(tempfilename);
+               return;
+       }
+       if (sock_gets(sock, buf) < 0) {
+               unlink(tempfilename);
+               return;
+       }
+       lprintf(9, "%s\n", buf);
+       sprintf(buf, "mv %s ./network/spoolin/%s.%ld",
+               tempfilename, remote_nodename, (long) getpid());
+       system(buf);
+}
+
+
+
+/*
+ * transmit network spool to the remote system
+ */
+void transmit_spool(int sock, char *remote_nodename)
+{
+       char buf[SIZ];
+       char pbuf[4096];
+       long plen;
+       long bytes_to_write, thisblock;
+       int fd;
+       char sfname[128];
+
+       if (sock_puts(sock, "NUOP") < 0) return;
+       if (sock_gets(sock, buf) < 0) return;
+       lprintf(9, "<%s\n", buf);
+       if (buf[0] != '2') {
+               return;
+       }
+
+       sprintf(sfname, "./network/spoolout/%s", remote_nodename);
+       fd = open(sfname, O_RDONLY);
+       if (fd < 0) {
+               if (errno == ENOENT) {
+                       lprintf(9, "Nothing to send.\n");
+               } else {
+                       lprintf(5, "cannot open upload file locally: %s\n",
+                               strerror(errno));
+               }
+               return;
+       }
+       while (plen = (long) read(fd, pbuf, IGNET_PACKET_SIZE), plen > 0L) {
+               bytes_to_write = plen;
+               while (bytes_to_write > 0L) {
+                       sprintf(buf, "WRIT %ld", bytes_to_write);
+                       if (sock_puts(sock, buf) < 0) {
+                               close(fd);
+                               return;
+                       }
+                       if (sock_gets(sock, buf) < 0) {
+                               close(fd);
+                               return;
+                       }
+                       thisblock = atol(&buf[4]);
+                       if (buf[0] == '7') {
+                               if (sock_write(sock, pbuf,
+                                  (int) thisblock) < 0) {
+                                       close(fd);
+                                       return;
+                               }
+                               bytes_to_write = bytes_to_write - thisblock;
+                       } else {
+                               goto ABORTUPL;
+                       }
+               }
+       }
+
+ABORTUPL:
+       close(fd);
+       if (sock_puts(sock, "UCLS 1") < 0) return;
+       if (sock_gets(sock, buf) < 0) return;
+       lprintf(9, "<%s\n", buf);
+       if (buf[0] == '2') {
+               unlink(sfname);
+       }
+}
+
+
+
+/*
+ * Poll one Citadel node (called by network_poll_other_citadel_nodes() below)
+ */
+void network_poll_node(char *node, char *secret, char *host, char *port) {
+       int sock;
+       char buf[SIZ];
+
+       lprintf(5, "Polling node <%s> at %s:%s\n", node, host, port);
+
+       sock = sock_connect(host, port, "tcp");
+       if (sock < 0) {
+               lprintf(7, "Could not connect: %s\n", strerror(errno));
+               return;
+       }
+       
+       lprintf(9, "Connected!\n");
+
+       /* Read the server greeting */
+       if (sock_gets(sock, buf) < 0) goto bail;
+       lprintf(9, ">%s\n", buf);
+
+       /* Identify ourselves */
+       sprintf(buf, "NETP %s|%s", config.c_nodename, secret);
+       lprintf(9, "<%s\n", buf);
+       if (sock_puts(sock, buf) <0) goto bail;
+       if (sock_gets(sock, buf) < 0) goto bail;
+       lprintf(9, ">%s\n", buf);
+       if (buf[0] != '2') goto bail;
+
+       /* At this point we are authenticated. */
+       receive_spool(sock, node);
+       transmit_spool(sock, node);
+
+       sock_puts(sock, "QUIT");
+bail:  sock_close(sock);
+}
+
+
+
+/*
+ * Poll other Citadel nodes and transfer inbound/outbound network data.
+ */
+void network_poll_other_citadel_nodes(void) {
+       char *ignetcfg = NULL;
+       int i;
+       char linebuf[SIZ];
+       char node[SIZ];
+       char host[SIZ];
+       char port[SIZ];
+       char secret[SIZ];
+
+       ignetcfg = CtdlGetSysConfig(IGNETCFG);
+       if (ignetcfg == NULL) return;   /* no nodes defined */
+
+       /* Use the string tokenizer to grab one line at a time */
+       for (i=0; i<num_tokens(ignetcfg, '\n'); ++i) {
+               extract_token(linebuf, ignetcfg, i, '\n');
+               extract(node, linebuf, 0);
+               extract(secret, linebuf, 1);
+               extract(host, linebuf, 2);
+               extract(port, linebuf, 3);
+               if ( (strlen(node) > 0) && (strlen(secret) > 0) 
+                  && (strlen(host) > 0) && strlen(port) > 0) {
+                       network_poll_node(node, secret, host, port);
+               }
+       }
+
+       phree(ignetcfg);
+}
+
+
+
+
+
+
+
 /*
  * network_do_queue()
  * 
@@ -833,6 +1067,14 @@ void network_do_queue(void) {
        doing_queue = 1;
        last_run = time(NULL);
 
+       /*
+        * Poll other Citadel nodes.
+        */
+       network_poll_other_citadel_nodes();
+
+       /*
+        * Load the network map into memory.
+        */
        read_network_map();
 
        /* 
@@ -859,6 +1101,39 @@ void network_do_queue(void) {
 }
 
 
+
+/*
+ * cmd_netp() - authenticate to the server as another Citadel node polling
+ *              for network traffic
+ */
+void cmd_netp(char *cmdbuf)
+{
+       char node[SIZ];
+       char pass[SIZ];
+
+       char secret[SIZ];
+       char nexthop[SIZ];
+
+       extract(node, cmdbuf, 0);
+       extract(pass, cmdbuf, 1);
+
+       if (is_valid_node(nexthop, secret, node) != 0) {
+               cprintf("%d authentication failed\n", ERROR);
+               return;
+       }
+
+       if (strcasecmp(pass, secret)) {
+               cprintf("%d authentication failed\n", ERROR);
+               return;
+       }
+
+       safestrncpy(CC->net_node, node, sizeof CC->net_node);
+       cprintf("%d authenticated as network node '%s'\n", OK,
+               CC->net_node);
+}
+
+
+
 /*
  * Module entry point
  */
@@ -866,6 +1141,7 @@ char *Dynamic_Module_Init(void)
 {
        CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
        CtdlRegisterProtoHook(cmd_snet, "SNET", "Get network config");
+       CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller");
        CtdlRegisterSessionHook(network_do_queue, EVT_TIMER);
        return "$Id$";
 }