X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fnetwork%2Fserv_netconfig.c;h=12662f066564a786d71137f963c92dcb3e14715b;hb=ba5374f1ab40a72e9af89d19e2901f6ac6ff7203;hp=970057133bb4e4c49dedbdf6bb92d453034e7c05;hpb=8b276ba2d09c1d606b6b282961c737b6b4e26d21;p=citadel.git diff --git a/citadel/modules/network/serv_netconfig.c b/citadel/modules/network/serv_netconfig.c index 970057133..12662f066 100644 --- a/citadel/modules/network/serv_netconfig.c +++ b/citadel/modules/network/serv_netconfig.c @@ -2,22 +2,16 @@ * This module handles shared rooms, inter-Citadel mail, and outbound * mailing list processing. * - * Copyright (c) 2000-2011 by the citadel.org team + * Copyright (c) 2000-2012 by the citadel.org team * * This program is open source 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. + * it under the terms of the GNU General Public License, version 3. * * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * * ** NOTE ** A word on the S_NETCONFIGS semaphore: * This is a fairly high-level type of critical section. It ensures that no * two threads work on the netconfigs files at the same time. Since we do @@ -81,200 +75,305 @@ #include "file_ops.h" #include "citadel_dirs.h" #include "threads.h" - -#ifndef HAVE_SNPRINTF -#include "snprintf.h" -#endif - #include "context.h" #include "netconfig.h" #include "netspool.h" #include "ctdl_module.h" -/* - * We build a map of network nodes during processing. - */ -NetMap *the_netmap = NULL; -int netmap_changed = 0; -char *working_ignetcfg = NULL; + +void DeleteNodeConf(void *vNode) +{ + NodeConf *Node = (NodeConf*) vNode; + FreeStrBuf(&Node->NodeName); + FreeStrBuf(&Node->Secret); + FreeStrBuf(&Node->Host); + FreeStrBuf(&Node->Port); + free(Node); +} + +NodeConf *NewNode(StrBuf *SerializedNode) +{ + const char *Pos = NULL; + NodeConf *Node; + + /* we need at least 4 pipes and some other text so its invalid. */ + if (StrLength(SerializedNode) < 8) + return NULL; + Node = (NodeConf *) malloc(sizeof(NodeConf)); + + Node->DeleteMe = 0; + + Node->NodeName=NewStrBuf(); + StrBufExtract_NextToken(Node->NodeName, SerializedNode, &Pos, '|'); + + Node->Secret=NewStrBuf(); + StrBufExtract_NextToken(Node->Secret, SerializedNode, &Pos, '|'); + + Node->Host=NewStrBuf(); + StrBufExtract_NextToken(Node->Host, SerializedNode, &Pos, '|'); + + Node->Port=NewStrBuf(); + StrBufExtract_NextToken(Node->Port, SerializedNode, &Pos, '|'); + return Node; +} + /* * Load or refresh the Citadel network (IGnet) configuration for this node. */ -void load_working_ignetcfg(void) { - char *cfg; - char *oldcfg; +HashList* load_ignetcfg(void) +{ + const char *LinePos; + char *Cfg; + StrBuf *Buf; + StrBuf *LineBuf; + HashList *Hash; + NodeConf *Node; + + Cfg = CtdlGetSysConfig(IGNETCFG); + if ((Cfg == NULL) || IsEmptyStr(Cfg)) { + if (Cfg != NULL) + free(Cfg); + return NULL; + } - cfg = CtdlGetSysConfig(IGNETCFG); - if (cfg == NULL) { - cfg = strdup(""); + Hash = NewHash(1, NULL); + Buf = NewStrBufPlain(Cfg, -1); + free(Cfg); + LineBuf = NewStrBufPlain(NULL, StrLength(Buf)); + LinePos = NULL; + do + { + StrBufSipLine(LineBuf, Buf, &LinePos); + if (StrLength(LineBuf) != 0) { + Node = NewNode(LineBuf); + if (Node != NULL) { + Put(Hash, SKEY(Node->NodeName), Node, DeleteNodeConf); + } + } + } while (LinePos != StrBufNOTNULL); + FreeStrBuf(&Buf); + FreeStrBuf(&LineBuf); + return Hash; +} + +void DeleteNetMap(void *vNetMap) +{ + NetMap *TheNetMap = (NetMap*) vNetMap; + FreeStrBuf(&TheNetMap->NodeName); + FreeStrBuf(&TheNetMap->NextHop); + free(TheNetMap); +} + +NetMap *NewNetMap(StrBuf *SerializedNetMap) +{ + const char *Pos = NULL; + NetMap *NM; + + /* we need at least 3 pipes and some other text so its invalid. */ + if (StrLength(SerializedNetMap) < 6) + return NULL; + NM = (NetMap *) malloc(sizeof(NetMap)); + + NM->NodeName=NewStrBuf(); + StrBufExtract_NextToken(NM->NodeName, SerializedNetMap, &Pos, '|'); + + NM->lastcontact = StrBufExtractNext_long(SerializedNetMap, &Pos, '|'); + + NM->NextHop=NewStrBuf(); + StrBufExtract_NextToken(NM->NextHop, SerializedNetMap, &Pos, '|'); + + return NM; +} + +HashList* read_network_map(void) +{ + const char *LinePos; + char *Cfg; + StrBuf *Buf; + StrBuf *LineBuf; + HashList *Hash; + NetMap *TheNetMap; + + Cfg = CtdlGetSysConfig(IGNETMAP); + if ((Cfg == NULL) || IsEmptyStr(Cfg)) { + if (Cfg != NULL) + free(Cfg); + return NULL; } - oldcfg = working_ignetcfg; - working_ignetcfg = cfg; - if (oldcfg != NULL) { - free(oldcfg); + Hash = NewHash(1, NULL); + Buf = NewStrBufPlain(Cfg, -1); + free(Cfg); + LineBuf = NewStrBufPlain(NULL, StrLength(Buf)); + LinePos = NULL; + while (StrBufSipLine(Buf, LineBuf, &LinePos)) + { + TheNetMap = NewNetMap(LineBuf); + if (TheNetMap != NULL) { /* TODO: is the NodeName Uniq? */ + Put(Hash, SKEY(TheNetMap->NodeName), TheNetMap, DeleteNetMap); + } } + FreeStrBuf(&Buf); + FreeStrBuf(&LineBuf); + return Hash; } -/* - * Read the network map from its configuration file into memory. - */ -void read_network_map(void) { - char *serialized_map = NULL; - int i; - char buf[SIZ]; - NetMap *nmptr; +StrBuf *SerializeNetworkMap(HashList *Map) +{ + void *vMap; + const char *key; + long len; + StrBuf *Ret = NewStrBuf(); + HashPos *Pos = GetNewHashPos(Map, 0); - serialized_map = CtdlGetSysConfig(IGNETMAP); - if (serialized_map == NULL) return; /* if null, no entries */ - - /* Use the string tokenizer to grab one line at a time */ - for (i=0; inodename, buf, 0, '|', sizeof nmptr->nodename); - nmptr->lastcontact = extract_long(buf, 1); - extract_token(nmptr->nexthop, buf, 2, '|', sizeof nmptr->nexthop); - nmptr->next = the_netmap; - the_netmap = nmptr; - } + while (GetNextHashPos(Map, Pos, &len, &key, &vMap)) + { + NetMap *pMap = (NetMap*) vMap; + StrBufAppendBuf(Ret, pMap->NodeName, 0); + StrBufAppendBufPlain(Ret, HKEY("|"), 0); - free(serialized_map); - netmap_changed = 0; + StrBufAppendPrintf(Ret, "%ld", pMap->lastcontact, 0); + StrBufAppendBufPlain(Ret, HKEY("|"), 0); + + StrBufAppendBuf(Ret, pMap->NextHop, 0); + StrBufAppendBufPlain(Ret, HKEY("\n"), 0); + } + DeleteHashPos(&Pos); + return Ret; } /* - * Write the network map from memory back to the configuration file. + * Learn topology from path fields */ -void write_network_map(void) { - char *serialized_map = NULL; +void network_learn_topology(char *node, char *path, HashList *the_netmap, int *netmap_changed) +{ + NetMap *pNM = NULL; + void *vptr; + char nexthop[256]; NetMap *nmptr; - - if (netmap_changed) { - serialized_map = strdup(""); - - if (the_netmap != NULL) { - for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) { - serialized_map = realloc(serialized_map, - (strlen(serialized_map)+SIZ) ); - if (!IsEmptyStr(nmptr->nodename)) { - snprintf(&serialized_map[strlen(serialized_map)], - SIZ, - "%s|%ld|%s\n", - nmptr->nodename, - (long)nmptr->lastcontact, - nmptr->nexthop); - } - } + if (GetHash(the_netmap, node, strlen(node), &vptr) && + (vptr != NULL))/* TODO: is the NodeName Uniq? */ + { + pNM = (NetMap*)vptr; + extract_token(nexthop, path, 0, '!', sizeof nexthop); + if (!strcmp(nexthop, ChrPtr(pNM->NextHop))) { + pNM->lastcontact = time(NULL); + (*netmap_changed) ++; + return; } - - CtdlPutSysConfig(IGNETMAP, serialized_map); - free(serialized_map); } - /* Now free the list */ - while (the_netmap != NULL) { - nmptr = the_netmap->next; - free(the_netmap); - the_netmap = nmptr; - } - netmap_changed = 0; + /* If we got here then it's not in the map, so add it. */ + nmptr = (NetMap *) malloc(sizeof (NetMap)); + nmptr->NodeName = NewStrBufPlain(node, -1); + nmptr->lastcontact = time(NULL); + nmptr->NextHop = NewStrBuf (); + StrBufExtract_tokenFromStr(nmptr->NextHop, path, strlen(path), 0, '!'); + /* TODO: is the NodeName Uniq? */ + Put(the_netmap, SKEY(nmptr->NodeName), nmptr, DeleteNetMap); + (*netmap_changed) ++; } -/* +/* * 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. If it *is* a neighbor node, we also fill in the * shared secret. */ -int is_valid_node(char *nexthop, char *secret, char *node) { - int i; - char linebuf[SIZ]; - char buf[SIZ]; - int retval; - NetMap *nmptr; +int is_valid_node(const StrBuf **nexthop, + const StrBuf **secret, + StrBuf *node, + HashList *IgnetCfg, + HashList *the_netmap) +{ + void *vNetMap; + void *vNodeConf; + NodeConf *TheNode; + NetMap *TheNetMap; - if (node == NULL) { + if (StrLength(node) == 0) { return(-1); } /* * First try the neighbor nodes */ - if (working_ignetcfg == NULL) { - syslog(LOG_ERR, "working_ignetcfg is NULL!\n"); + if (GetCount(IgnetCfg) == 0) { + syslog(LOG_INFO, "IgnetCfg is empty!\n"); if (nexthop != NULL) { - strcpy(nexthop, ""); + *nexthop = NULL; } return(-1); } - retval = (-1); - if (nexthop != NULL) { - strcpy(nexthop, ""); - } - - /* Use the string tokenizer to grab one line at a time */ - for (i=0; iSecret; + return 0; /* yup, it's a direct neighbor */ } - /* + /* * If we get to this point we have to see if we know the next hop - */ - if (the_netmap != NULL) { - for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) { - if (!strcasecmp(nmptr->nodename, node)) { - if (nexthop != NULL) { - strcpy(nexthop, nmptr->nexthop); - } - return(0); - } - } + *//* TODO: is the NodeName Uniq? */ + if ((GetCount(the_netmap) > 0) && + (GetHash(the_netmap, SKEY(node), &vNetMap))) + { + TheNetMap = (NetMap*)vNetMap; + if (nexthop != NULL) + *nexthop = TheNetMap->NextHop; + return(0); } /* * If we get to this point, the supplied node name is bogus. */ - syslog(LOG_ERR, "Invalid node name <%s>\n", node); + syslog(LOG_ERR, "Invalid node name <%s>\n", ChrPtr(node)); return(-1); } - -void cmd_gnet(char *argbuf) { +void cmd_gnet(char *argbuf) +{ char filename[PATH_MAX]; char buf[SIZ]; FILE *fp; - if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) { - /* users can edit the netconfigs for their own mailbox rooms */ - } - else if (CtdlAccessCheck(ac_room_aide)) return; - assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir); - cprintf("%d Network settings for room #%ld <%s>\n", - LISTING_FOLLOWS, - CC->room.QRnumber, CC->room.QRname); + if (!IsEmptyStr(argbuf)) + { + if (CtdlAccessCheck(ac_aide)) return; + if (strcmp(argbuf, FILE_MAILALIAS)) + { + cprintf("%d No such file or directory\n", + ERROR + INTERNAL_ERROR); + return; + } + safestrncpy(filename, file_mail_aliases, sizeof(filename)); + cprintf("%d Settings for <%s>\n", + LISTING_FOLLOWS, + filename); + } + else + { + if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) { + /* users can edit the netconfigs for their own mailbox rooms */ + } + else if (CtdlAccessCheck(ac_room_aide)) return; + + assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir); + cprintf("%d Network settings for room #%ld <%s>\n", + LISTING_FOLLOWS, + CC->room.QRnumber, CC->room.QRname); + } fp = fopen(filename, "r"); if (fp != NULL) { @@ -288,6 +387,14 @@ void cmd_gnet(char *argbuf) { cprintf("000\n"); } +#define nForceAliases 5 +const ConstStr ForceAliases[nForceAliases] = { + {HKEY("bbs,")}, + {HKEY("root,")}, + {HKEY("Auto,")}, + {HKEY("postmaster,")}, + {HKEY("abuse,")} +}; void cmd_snet(char *argbuf) { char tempfilename[PATH_MAX]; @@ -297,17 +404,35 @@ void cmd_snet(char *argbuf) { struct stat StatBuf; long len; int rc; + int IsMailAlias = 0; + int MailAliasesFound[nForceAliases]; unbuffer_output(); - if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) { - /* users can edit the netconfigs for their own mailbox rooms */ + if (!IsEmptyStr(argbuf)) + { + if (CtdlAccessCheck(ac_aide)) return; + if (strcmp(argbuf, FILE_MAILALIAS)) + { + cprintf("%d No such file or directory\n", + ERROR + INTERNAL_ERROR); + return; + } + len = safestrncpy(filename, file_mail_aliases, sizeof(filename)); + memset(MailAliasesFound, 0, sizeof(MailAliasesFound)); + memcpy(tempfilename, filename, len + 1); + IsMailAlias = 1; + } + else + { + if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) { + /* users can edit the netconfigs for their own mailbox rooms */ + } + else if (CtdlAccessCheck(ac_room_aide)) return; + + len = assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir); + memcpy(tempfilename, filename, len + 1); } - else if (CtdlAccessCheck(ac_room_aide)) return; - - len = assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir); - memcpy(tempfilename, filename, len + 1); - memset(&StatBuf, 0, sizeof(struct stat)); if ((stat(filename, &StatBuf) == -1) || (StatBuf.st_size == 0)) StatBuf.st_size = 80; /* Not there or empty? guess 80 chars line. */ @@ -352,6 +477,24 @@ void cmd_snet(char *argbuf) { { if ((rc == 3) && (strcmp(ChrPtr(Line), "000") == 0)) break; + if (IsMailAlias) + { + int i; + + for (i = 0; i < nForceAliases; i++) + { + if ((!MailAliasesFound[i]) && + (strncmp(ForceAliases[i].Key, + ChrPtr(Line), + ForceAliases[i].len) == 0) + ) + { + MailAliasesFound[i] = 1; + break; + } + } + } + StrBufAppendBufPlain(Line, HKEY("\n"), 0); write(TmpFD, ChrPtr(Line), StrLength(Line)); len += StrLength(Line); @@ -360,6 +503,28 @@ void cmd_snet(char *argbuf) { ftruncate(TmpFD, len); close(TmpFD); + if (IsMailAlias) + { + int i, state; + /* + * Sanity check whether all aliases required by the RFCs were set + * else bail out. + */ + state = 1; + for (i = 0; i < nForceAliases; i++) + { + if (!MailAliasesFound[i]) + state = 0; + } + if (state == 0) + { + cprintf("%d won't do this - you're missing an RFC required alias.\n", + ERROR + INTERNAL_ERROR); + unlink(tempfilename); + return; + } + } + /* Now copy the temp file to its permanent location. * (We copy instead of link because they may be on different filesystems) */ @@ -374,56 +539,90 @@ void cmd_snet(char *argbuf) { */ void cmd_netp(char *cmdbuf) { - char node[256]; - char pass[256]; + struct CitContext *CCC = CC; + HashList *working_ignetcfg; + char *node; + StrBuf *NodeStr; + long nodelen; int v; + long lens[2]; + const char *strs[2]; - char secret[256]; - char nexthop[256]; - char err_buf[SIZ]; + const StrBuf *secret = NULL; + const StrBuf *nexthop = NULL; + char err_buf[SIZ] = ""; /* Authenticate */ - extract_token(node, cmdbuf, 0, '|', sizeof node); - extract_token(pass, cmdbuf, 1, '|', sizeof pass); - + node = CCC->curr_user; + nodelen = extract_token(CCC->curr_user, cmdbuf, 0, '|', sizeof CCC->curr_user); + NodeStr = NewStrBufPlain(node, nodelen); /* load the IGnet Configuration to check node validity */ - load_working_ignetcfg(); - v = is_valid_node(nexthop, secret, node); - + working_ignetcfg = load_ignetcfg(); + v = is_valid_node(&nexthop, &secret, NodeStr, working_ignetcfg, NULL); if (v != 0) { snprintf(err_buf, sizeof err_buf, "An unknown Citadel server called \"%s\" attempted to connect from %s [%s].\n", - node, CC->cs_host, CC->cs_addr + node, CCC->cs_host, CCC->cs_addr ); syslog(LOG_WARNING, "%s", err_buf); cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED); - CtdlAideMessage(err_buf, "IGNet Networking."); + + strs[0] = CCC->cs_addr; + lens[0] = strlen(CCC->cs_addr); + + strs[1] = "SRV_UNKNOWN"; + lens[1] = sizeof("SRV_UNKNOWN" - 1); + + CtdlAideFPMessage( + err_buf, + "IGNet Networking.", + 2, strs, (long*) &lens); + + DeleteHash(&working_ignetcfg); + FreeStrBuf(&NodeStr); return; } - if (strcasecmp(pass, secret)) { + extract_token(CCC->user.password, cmdbuf, 1, '|', sizeof CCC->user.password); + if (strcasecmp(CCC->user.password, ChrPtr(secret))) { snprintf(err_buf, sizeof err_buf, "A Citadel server at %s [%s] failed to authenticate as network node \"%s\".\n", - CC->cs_host, CC->cs_addr, node + CCC->cs_host, CCC->cs_addr, node ); syslog(LOG_WARNING, "%s", err_buf); cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED); - CtdlAideMessage(err_buf, "IGNet Networking."); + + strs[0] = CCC->cs_addr; + lens[0] = strlen(CCC->cs_addr); + + strs[1] = "SRV_PW"; + lens[1] = sizeof("SRV_PW" - 1); + + CtdlAideFPMessage( + err_buf, + "IGNet Networking.", + 2, strs, (long*) &lens); + + DeleteHash(&working_ignetcfg); + FreeStrBuf(&NodeStr); return; } - if (network_talking_to(node, NTT_CHECK)) { + if (network_talking_to(node, nodelen, NTT_CHECK)) { syslog(LOG_WARNING, "Duplicate session for network node <%s>", node); cprintf("%d Already talking to %s right now\n", ERROR + RESOURCE_BUSY, node); + DeleteHash(&working_ignetcfg); + FreeStrBuf(&NodeStr); return; } - - safestrncpy(CC->net_node, node, sizeof CC->net_node); - network_talking_to(node, NTT_ADD); + nodelen = safestrncpy(CCC->net_node, node, sizeof CCC->net_node); + network_talking_to(CCC->net_node, nodelen, NTT_ADD); syslog(LOG_NOTICE, "Network node <%s> logged in from %s [%s]\n", - CC->net_node, CC->cs_host, CC->cs_addr + CCC->net_node, CCC->cs_host, CCC->cs_addr ); - cprintf("%d authenticated as network node '%s'\n", CIT_OK, CC->net_node); + cprintf("%d authenticated as network node '%s'\n", CIT_OK, CCC->net_node); + DeleteHash(&working_ignetcfg); + FreeStrBuf(&NodeStr); } int netconfig_check_roomaccess(