2 * This module polls other Citadel servers for inter-site networking.
4 * Copyright (c) 2000-2017 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * ** NOTE ** A word on the S_NETCONFIGS semaphore:
15 * This is a fairly high-level type of critical section. It ensures that no
16 * two threads work on the netconfigs files at the same time. Since we do
17 * so many things inside these, here are the rules:
18 * 1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
19 * 2. Do *not* perform any I/O with the client during these sections.
34 #include <sys/types.h>
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
41 # include <sys/time.h>
49 # if HAVE_SYS_SYSCALL_H
50 # include <sys/syscall.h>
57 #include <libcitadel.h>
60 #include "citserver.h"
66 #include "internet_addressing.h"
67 #include "clientsocket.h"
68 #include "citadel_dirs.h"
71 #include "ctdl_module.h"
73 struct CitContext networker_client_CC;
77 * Poll one Citadel node (the Citadel node name, host/ip, port number, and shared secret are supplied by the caller)
79 void network_poll_node(StrBuf *node, StrBuf *host, StrBuf *port, StrBuf *secret)
82 CC->SBuf.Buf = NewStrBuf();
83 CC->sMigrateBuf = NewStrBuf();
84 CC->SBuf.ReadWritePointer = NULL;
88 StrBuf *SpoolFileName = NULL;
90 syslog(LOG_DEBUG, "netpoll: polling %s at %s:%s", ChrPtr(node), ChrPtr(host), ChrPtr(port));
92 int sock = sock_connect((char *)ChrPtr(host), (char *)ChrPtr(port));
94 syslog(LOG_ERR, "%s: %s", ChrPtr(host), strerror(errno));
98 /* Read the server greeting */
99 if (sock_getln(&sock, buf, sizeof buf) < 0) {
103 /* Check that the remote is who we think it is and warn the site admin if not */
104 if (strncmp(&buf[4], ChrPtr(node), StrLength(node))) {
105 CtdlAideMessage(buf, "Connected to wrong node!");
106 syslog(LOG_ERR, "netpoll: was expecting node <%s> but got %s", ChrPtr(node), buf);
110 /* We're talking to the correct node. Now identify ourselves. */
111 snprintf(buf, sizeof buf, "NETP %s|%s", CtdlGetConfigStr("c_nodename"), ChrPtr(secret));
112 sock_puts(&sock, buf);
113 if (sock_getln(&sock, buf, sizeof buf) < 0) {
117 CtdlAideMessage(buf, "Could not authenticate to network peer");
118 syslog(LOG_ERR, "netpoll: could not authenticate to <%s> : %s", ChrPtr(node), buf);
122 /* Tell it we want to download anything headed our way. */
123 sock_puts(&sock, "NDOP");
124 if (sock_getln(&sock, buf, sizeof buf) < 0) {
128 CtdlAideMessage(buf, "NDOP error");
129 syslog(LOG_ERR, "netpoll: NDOP error talking to <%s> : %s", ChrPtr(node), buf);
133 bytes_total = atoi(&buf[4]);
136 SpoolFileName = NewStrBuf();
137 StrBufPrintf(SpoolFileName, // Incoming packets get dropped into the "spoolin/" directory
144 StrBufStripSlashes(SpoolFileName, 1);
146 FILE *netinfp = fopen(ChrPtr(SpoolFileName), "w");
147 FreeStrBuf(&SpoolFileName);
152 while (bytes_read < bytes_total) {
153 snprintf(buf, sizeof buf, "READ %d|%d", bytes_read, bytes_total-bytes_read);
154 sock_puts(&sock, buf);
155 if (sock_getln(&sock, buf, sizeof buf) < 0) {
160 this_block = atoi(&buf[4]);
162 // Use buffered reads to download the data from remote server
163 StrBuf *ThisBlockBuf = NewStrBuf();
164 int blen = socket_read_blob(&sock, ThisBlockBuf, this_block, 20);
166 fwrite(ChrPtr(ThisBlockBuf), blen, 1, netinfp);
169 FreeStrBuf(&ThisBlockBuf);
170 if (blen < this_block) {
171 syslog(LOG_DEBUG, "netpoll: got short block, ftn");
178 syslog(LOG_DEBUG, "netpoll: downloaded %d of %d bytes from %s", bytes_read, bytes_total, ChrPtr(node));
180 if (fclose(netinfp) == 0) {
181 sock_puts(&sock, "CLOS"); // CLOSing the download causes it to be deleted on the other node
182 if (sock_getln(&sock, buf, sizeof buf) < 0) {
187 // Now get ready to send our network data to the other node.
188 SpoolFileName = NewStrBuf();
189 StrBufPrintf(SpoolFileName, // Outgoing packets come from the "spoolout/" directory
194 FILE *netoutfp = fopen(ChrPtr(SpoolFileName), "w");
195 FreeStrBuf(&SpoolFileName);
203 FreeStrBuf(&CC->SBuf.Buf);
204 FreeStrBuf(&CC->sMigrateBuf);
209 * Poll other Citadel nodes and transfer inbound/outbound network data.
210 * Set "full" to nonzero to force a poll of every node, or to zero to poll
211 * only nodes to which we have data to send.
213 void network_poll_other_citadel_nodes(int full_poll, HashList *ignetcfg)
219 StrBuf *SpoolFileName;
223 if (GetCount(ignetcfg) ==0) {
224 syslog(LOG_DEBUG, "netpoll: no neighbor nodes are configured - not polling");
227 become_session(&networker_client_CC);
229 SpoolFileName = NewStrBufPlain(ctdl_netout_dir, -1);
231 Pos = GetNewHashPos(ignetcfg, 0);
233 while (GetNextHashPos(ignetcfg, Pos, &len, &key, &vCfg))
235 /* Use the string tokenizer to grab one line at a time */
236 if (server_shutting_down) {
239 CtdlNodeConf *pNode = (CtdlNodeConf*) vCfg;
242 StrBuf *node = NewStrBufDup(pNode->NodeName);
243 StrBuf *host = NewStrBufDup(pNode->Host);
244 StrBuf *port = NewStrBufDup(pNode->Port);
245 StrBuf *secret = NewStrBufDup(pNode->Secret);
247 if ( (StrLength(node) != 0) &&
248 (StrLength(secret) != 0) &&
249 (StrLength(host) != 0)
254 StrBufAppendBufPlain(SpoolFileName, HKEY("/"), 0);
255 StrBufAppendBuf(SpoolFileName, node, 0);
256 StrBufStripSlashes(SpoolFileName, 1);
258 if (access(ChrPtr(SpoolFileName), R_OK) == 0) {
263 if (poll && (StrLength(host) > 0) && strcmp("0.0.0.0", ChrPtr(host))) {
264 if (!CtdlNetworkTalkingTo(SKEY(node), NTT_CHECK)) {
265 network_poll_node(node, host, port, secret);
273 FreeStrBuf(&SpoolFileName);
278 void network_do_clientqueue(void)
280 HashList *working_ignetcfg;
281 int full_processing = 1;
282 static time_t last_run = 0L;
285 * Run the full set of processing tasks no more frequently
286 * than once every n seconds
288 if ( (time(NULL) - last_run) < CtdlGetConfigLong("c_net_freq") )
291 syslog(LOG_DEBUG, "netpoll: full processing in %ld seconds.",
292 CtdlGetConfigLong("c_net_freq") - (time(NULL)- last_run)
296 working_ignetcfg = CtdlLoadIgNetCfg();
298 * Poll other Citadel nodes. Maybe. If "full_processing" is set
299 * then we poll everyone. Otherwise we only poll nodes we have stuff
302 network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
303 DeleteHash(&working_ignetcfg);
309 CTDL_MODULE_INIT(network_client)
313 CtdlFillSystemContext(&networker_client_CC, "CitNetworker");
314 CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER, PRIO_SEND + 10);
317 return "networkclient";