Преглед изворни кода

Merge branch 'localhubs'

* localhubs: (44 commits)
  * Don't require hostname matching on unix domain sockets
  * Share protected user entries with localhubs for their children
  * When auto adding hostname, do not consider -telnet! hostnames as conflicting.
  * Add note about localhub linking
  * Rename sun to sock_un as solaris seems to have 'sun' as a reserved constant
  * Don't consider hub linked until a userfile has been downloaded.
  * When restructuring for update, be sure to unlink the actual uplink, not the first bot.
  * socklen should be socklen_t
  * Only delay links to unix domain sockets - children bots
  * Delay leaf linking to localhub until it has received a userfile
  * Fix regression in opening a listen port
  * Increase child leaf bot reconnect time to 5 seconds - hubs/localhubs still go on a 30 second cycle
  * Fix sun addrlen
  * Fix compiler warnings
  * Fix possible crash in fatal()
  * Sync localhub userlist changes back to hub after link
  * Lookup bots in userlist again before booting as invalid
  * Localhub: Don't share out VAR_NOLDEF vars to child bots until linked to a hub
  * Linking log fix
  * Localhub: Share new userfile with child bots once it has been (re)loaded from hub
  ...
Bryan Drewery пре 16 година
родитељ
комит
dbd12a770c
25 измењених фајлова са 525 додато и 216 уклоњено
  1. 2 0
      doc/UPDATES
  2. 14 1
      src/binary.c
  3. 44 23
      src/botcmd.c
  4. 72 29
      src/botnet.c
  5. 66 16
      src/chanprog.c
  6. 1 0
      src/chanprog.h
  7. 1 5
      src/cmds.c
  8. 20 11
      src/conf.c
  9. 1 0
      src/conf.h
  10. 104 53
      src/dcc.c
  11. 1 0
      src/dcc.h
  12. 1 1
      src/dccutil.c
  13. 4 1
      src/enclink.c
  14. 6 2
      src/main.c
  15. 1 1
      src/main.h
  16. 7 0
      src/misc.c
  17. 7 3
      src/mod/server.mod/server.c
  18. 22 7
      src/mod/share.mod/share.c
  19. 8 4
      src/mod/update.mod/update.c
  20. 97 44
      src/net.c
  21. 5 2
      src/net.h
  22. 12 3
      src/set.c
  23. 9 6
      src/userent.c
  24. 4 1
      src/userrec.c
  25. 16 3
      src/users.c

+ 2 - 0
doc/UPDATES

@@ -1,3 +1,5 @@
+* Bots now link to the first bot in their binary (the localhub) over a UNIX domain socket. Only the localhub connects to the main hubs.
+
 1.2.17 - http://wraith.botpack.net/milestone/1.2.17
 * Binary / shell / startup changes
   * Binary error messages are no longer obscure numbers or fake segfaults. (Compile with OBSCURE_ERRORS to re-enable)

+ 14 - 1
src/binary.c

@@ -17,6 +17,7 @@
 #include "misc_file.h"
 #include "tandem.h"
 #include "botnet.h"
+#include "net.h"
 #include "userrec.h"
 
 #include <sys/wait.h>
@@ -755,8 +756,20 @@ void reload_bin_data() {
     if (oldbots)
       free_conf_bots(oldbots);
 
-    if (!conf.bot->localhub)
+    if (!conf.bot->localhub) {
       free_conf_bots(conf.bots);
+
+      if (was_localhub) {
+        //Close the listening port
+        for (int i = 0; i < dcc_total; i++) {
+          if (dcc[i].type && (dcc[i].type == &DCC_TELNET) && (strchr(dcc[i].host, '/'))) {
+              unlink(dcc[i].host);
+              killsock(dcc[i].sock);
+              lostdcc(i);
+          }
+        }
+      }
+    }
   }
 }
 

+ 44 - 23
src/botcmd.c

@@ -539,7 +539,7 @@ static void bot_log(int idx, char *par)
   if (egg_isdigit(par[0])) {
     int type = atoi(newsplit(&par));
 
-    if (conf.bot->hub)
+    if (conf.bot->hub || conf.bot->localhub)
       botnet_send_log(idx, from, type, par);
 
     putlog(type, "@", "(%s) %s", from, par);
@@ -812,7 +812,7 @@ static void bot_timesync(int idx, char *par)
 //  putlog(LOG_DEBUG, "@", "Got timesync from %s: %s (%li - %li)", dcc[idx].nick, par, atol(par), now);
   timesync = atol(par) - now;
 
-  if (conf.bot->hub)
+  if (conf.bot->hub || conf.bot->localhub)
     send_timesync(-1);
 }
 
@@ -836,31 +836,52 @@ static void bot_reject(int idx, char *par)
     return;
   }
   who = newsplit(&par);
-  destbot = strchr(who, '@');
-  *destbot++ = 0;
-  if (!strcasecmp(destbot, conf.bot->nick)) {
-    /* Kick someone here! */
-    int ok = 0;
-
-    for (i = 0; i < dcc_total; i++) {
-      if (dcc[i].type && dcc[i].simul == -1 && !strcasecmp(who, dcc[i].nick) && (dcc[i].type->flags & DCT_CHAT)) {
-        u = get_user_by_handle(userlist, from);
-        if (u) {
-          if (!whois_access(u, dcc[idx].user)) {
-            add_note(from, conf.bot->nick, "Sorry, you cannot boot them.", -1, 0);
-            return;
+  if (!(destbot = strchr(who, '@'))) {
+    /* Rejecting a bot */
+    i = nextbot(who);
+    if (i < 0) {
+      botnet_send_priv(idx, conf.bot->nick, from, NULL, "Can't unlink %s (doesn't exist)", who);
+    } else if (!strcasecmp(dcc[i].nick, who)) {
+      char s[1024];
+
+      /* I'm the connection to the rejected bot */
+      putlog(LOG_BOTS, "*", "%s rejected %s", from, dcc[i].nick);
+      dprintf(i, "bye %s\n", par[0] ? par : "rejected");
+      simple_sprintf(s, "Disconnected %s (%s: %s)", dcc[i].nick, from, par[0] ? par : "rejected");
+      chatout("*** %s\n", s);
+      botnet_send_unlinked(i, dcc[i].nick, s);
+      killsock(dcc[i].sock);
+      lostdcc(i);
+    } else {
+      if (i >= 0)
+        botnet_send_reject(i, from, NULL, who, NULL, par);
+    }
+  } else {                      /* Rejecting user */
+    *destbot++ = 0;
+    if (!strcasecmp(destbot, conf.bot->nick)) {
+      /* Kick someone here! */
+      int ok = 0;
+
+      for (i = 0; i < dcc_total; i++) {
+        if (dcc[i].type && dcc[i].simul == -1 && !strcasecmp(who, dcc[i].nick) && (dcc[i].type->flags & DCT_CHAT)) {
+          u = get_user_by_handle(userlist, from);
+          if (u) {
+            if (!whois_access(u, dcc[idx].user)) {
+              add_note(from, conf.bot->nick, "Sorry, you cannot boot them.", -1, 0);
+              return;
+            }
+            do_boot(i, from, par);
+            putlog(LOG_CMDS, "*", "#%s# boot %s (%s)", from, who, par[0] ? par : "No reason");
+            ok = 1;
           }
-          do_boot(i, from, par);
-          putlog(LOG_CMDS, "*", "#%s# boot %s (%s)", from, who, par[0] ? par : "No reason");
-          ok = 1;
         }
       }
+    } else {
+      i = nextbot(destbot);
+      *--destbot = '@';
+      if (i >= 0)
+        botnet_send_reject(i, from, NULL, who, NULL, par);
     }
-  } else {
-    i = nextbot(destbot);
-    *--destbot = '@';
-    if (i >= 0)
-      botnet_send_reject(i, from, NULL, who, NULL, par);
   }
 }
 

+ 72 - 29
src/botnet.c

@@ -765,7 +765,7 @@ void dump_links(int z)
   register size_t l;
   char x[1024] = "";
 
-  if (conf.bot->hub) {
+  if (conf.bot->hub || conf.bot->localhub) {
     tand_t *bot = NULL;
     char *p = NULL;
 
@@ -948,6 +948,7 @@ int botunlink(int idx, const char *nick, const char *reason)
 }
 
 static void botlink_dns_callback(int, void *, const char *, bd::Array<bd::String>);
+static void botlink_real(int);
 
 /* Link to another bot
  */
@@ -976,10 +977,16 @@ int botlink(char *linker, int idx, char *nick)
 	return 0;
       }
     }
+
+    bool unix_domain = 0;
+
+    if (!conf.bot->hub && !conf.bot->localhub && !strcmp(nick, conf.localhub))
+      unix_domain = 1;
+
     /* Address to connect to is in 'info' */
     struct bot_addr *bi = (struct bot_addr *) get_user(&USERENTRY_BOTADDR, u);
 
-    if (!bi || !strlen(bi->address) || !bi->telnet_port || (bi->telnet_port <= 0)) {
+    if (!unix_domain && (!bi || !strlen(bi->address) || !bi->telnet_port || (bi->telnet_port <= 0))) {
       if (idx >= 0) {
 	dprintf(idx, "Invalid telnet address:port stored for '%s'.\n", nick);
 	dprintf(idx, "Use: %schaddr %s <address>:<port#>[/<relay-port#>]\n", (dcc[idx].u.chat->channel >= 0) ? settings.dcc_prefix : "", nick);
@@ -990,30 +997,47 @@ int botlink(char *linker, int idx, char *nick)
     } else {
       correct_handle(nick);
 
-      if (idx > -2)
-	putlog(LOG_BOTS, "*", "Linking to %s at %s:%d ...", nick,
-	       bi->address, bi->telnet_port);
+      char *address = NULL;
+      port_t port = 0;
+
+      if (unix_domain) {
+        address = conf.localhub_socket;
+      } else if (bi) {
+        address = bi->address;
+        port = bi->telnet_port;
+      }
+
+      if (idx > -2) {
+        if (port)
+          putlog(LOG_BOTS, "*", "Linking to %s at %s:%d ...", nick, address, port);
+        else
+          putlog(LOG_BOTS, "*", "Linking to %s at %s ...", nick, address);
+      }
 
       i = new_dcc(&DCC_DNSWAIT, sizeof(struct dns_info));
 
       dcc[i].timeval = now;
-      dcc[i].port = bi->telnet_port;
+      dcc[i].port = port;
       dcc[i].user = u;
       strlcpy(dcc[i].nick, nick, NICKLEN);
-      strlcpy(dcc[i].host, bi->address, UHOSTLEN);
+      strlcpy(dcc[i].host, address, UHOSTLEN);
       dcc[i].u.dns->cptr = strdup(linker);
       dcc[i].u.dns->ibuf = idx;
       dcc[i].bot = 1;
 
-      int dns_id = egg_dns_lookup(bi->address, 20, botlink_dns_callback, (void *) (long)i);
-       /* dns_id 
-        * -1 means it was cached and the callback already called
-        * -2 means it's already being looked up.. try again later .. */
-      if (dns_id >= 0)
-        dcc[i].dns_id = dns_id;
-      else if (dns_id == -2) {
-        lostdcc(i);
-        return 0;
+      if (unix_domain) {
+        botlink_real(i);
+      } else {
+        int dns_id = egg_dns_lookup(address, 20, botlink_dns_callback, (void *) (long)i);
+         /* dns_id
+          * -1 means it was cached and the callback already called
+          * -2 means it's already being looked up.. try again later .. */
+        if (dns_id >= 0)
+          dcc[i].dns_id = dns_id;
+        else if (dns_id == -2) {
+          lostdcc(i);
+          return 0;
+        }
       }
      
       return 1;
@@ -1031,15 +1055,9 @@ static void botlink_dns_callback(int id, void *client_data, const char *host, bd
   if (!valid_dns_id(i, id))
     return;
 
-  int idx = -1;
-  char *linker = NULL;
-
-  if (valid_idx(i)) {
-    idx = dcc[i].u.dns->ibuf;
-    linker = strdup(dcc[i].u.dns->cptr);
-    free(dcc[i].u.dns->cptr);
-    dcc[i].u.dns->cptr = NULL;
-  }
+//  if (valid_idx(i)) {
+//    idx = dcc[i].u.dns->ibuf;
+//  }
 
   if (!ips.size()) {
     lostdcc(i);
@@ -1047,6 +1065,17 @@ static void botlink_dns_callback(int id, void *client_data, const char *host, bd
   }
 
   dcc[i].addr = inet_addr(bd::String(ips[0]).c_str());
+  strlcpy(dcc[i].host, bd::String(ips[0]).c_str(), UHOSTLEN);
+
+  botlink_real(i);
+}
+
+static void botlink_real(int i)
+{
+  int idx = dcc[i].u.dns->ibuf;
+  char *linker = strdup(dcc[i].u.dns->cptr);
+  free(dcc[i].u.dns->cptr);
+  dcc[i].u.dns->cptr = NULL;
 
   changeover_dcc(i, &DCC_FORK_BOT, sizeof(struct bot_info));
   dcc[i].timeval = now;
@@ -1059,7 +1088,12 @@ static void botlink_dns_callback(int id, void *client_data, const char *host, bd
 
   dcc[i].u.bot->port = dcc[i].port;             /* Remember where i started */
 #ifdef USE_IPV6
-  dcc[i].sock = getsock(SOCK_STRONGCONN, is_dotted_ip(bd::String(ips[0]).c_str()));
+  int af_type;
+  if (dcc[i].port)
+    af_type = is_dotted_ip(dcc[i].host);
+  else
+    af_type = AF_UNIX;
+  dcc[i].sock = getsock(SOCK_STRONGCONN, af_type);
 #else
   dcc[i].sock = getsock(SOCK_STRONGCONN);
 #endif /* USE_IPV6 */
@@ -1067,9 +1101,12 @@ static void botlink_dns_callback(int id, void *client_data, const char *host, bd
 //  if (dcc[i].sock > 0)
 //    identd_open();                      /* will be closed when an ident is replied. */
 
-  if (dcc[i].sock < 0 || open_telnet_raw(dcc[i].sock, bd::String(ips[0]).c_str(), dcc[i].port, 0, 1) < 0)
+  int open_telnet_return = 0;
+  if (dcc[i].sock < 0 || (open_telnet_return = open_telnet_raw(dcc[i].sock, dcc[i].host, dcc[i].port, 0, 1)) < 0) {
+    if (open_telnet_return == -1)
+      dcc[i].sock = -1;
     failed_link(i);
-  else { /* let's attempt to initiate SSL before ANYTHING else... */
+  } else { /* let's attempt to initiate SSL before ANYTHING else... */
     dcc[i].ssl = 0;
   }
 
@@ -1238,8 +1275,10 @@ static void tandem_relay_dns_callback(int id, void *client_data, const char *hos
   dcc[i].u.relay->chat->current_lines = 0;
   dcc[i].timeval = now;
 
-  if (open_telnet_raw(dcc[i].sock, bd::String(ips[0]).c_str(), dcc[i].port, 0) < 0)
+  if (open_telnet_raw(dcc[i].sock, bd::String(ips[0]).c_str(), dcc[i].port, 0) < 0) {
+    dcc[i].sock = -1;
     failed_tandem_relay(i);
+  }
 }
 
 /* Input from user before connect is ready
@@ -1609,9 +1648,12 @@ void check_botnet_pings()
    if (dcc[i].type) {
      top_index = i;
     if (dcc[i].type == &DCC_BOT) {
+      // Hubs only allow localhubs to link, which CAN link bots now, so this isn't so cut and dry now
+#ifdef no
       if (dcc[i].status & STAT_LEAF) {
         tand_t *via = findbot(dcc[i].nick);
 
+        // Check if this leaf has any linked bots
         for (bot = tandbot; bot; bot = bot->next) {
           if ((via == bot->via) && (bot != via)) {
 	    /* Not leaflike behavior */
@@ -1639,6 +1681,7 @@ void check_botnet_pings()
 	    dcc[i].status &= ~STAT_WARNED;
         }
       }
+#endif
 
       if (dcc[i].status & STAT_PINGED) {
         char s[1024] = "";

+ 66 - 16
src/chanprog.c

@@ -523,29 +523,42 @@ void load_internal_users()
 
 }
 
-void add_myself_to_userlist() {
-  struct bot_addr *bi = NULL;
-
-  if (!(conf.bot->u = get_user_by_handle(userlist, conf.bot->nick))) {
+static struct userrec* add_bot_userlist(char* bot) {
+  struct userrec *u = NULL;
+  if (!(u = get_user_by_handle(userlist, bot))) {
     /* I need to be on the userlist... doh. */
-    userlist = adduser(userlist, conf.bot->nick, "none", "-", USER_OP, 1);
-    conf.bot->u = get_user_by_handle(userlist, conf.bot->nick);
+    userlist = adduser(userlist, bot, "none", "-", USER_OP, 1);
+    u = get_user_by_handle(userlist, bot);
+
+    struct bot_addr *bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
     bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
+    bi->uplink = (char *) my_calloc(1, 1);
+    bi->address = (char *) my_calloc(1, 1);
+    bi->telnet_port = 3333;
+    bi->relay_port = 3333;
+    bi->hublevel = 999;
+    set_user(&USERENTRY_BOTADDR, u, bi);
+  }
+  return u;
+}
 
-    /* Assume hub has a record added from load_internal_users();
-       why would it think it was a hub if it wasn't in the hub list??
-    */
-    if (!conf.bot->hub) {
-      if (conf.bot->net.ip)
-        bi->address = strdup(conf.bot->net.ip);
-      bi->telnet_port = bi->relay_port = 3333;
-      bi->hublevel = 999;
-      bi->uplink = (char *) my_calloc(1, 1);
-      set_user(&USERENTRY_BOTADDR, conf.bot->u, bi);
+void add_myself_to_userlist() {
+  conf.bot->u = add_bot_userlist(conf.bot->nick);
+}
+
+void add_child_bots() {
+  conf_bot* bot = conf.bots->next; //Skip myself
+  if (bot && bot->nick) {
+    for (; bot && bot->nick; bot = bot->next) {
+      add_bot_userlist(bot->nick);
     }
   }
 }
 
+void add_localhub() {
+  add_bot_userlist(conf.localhub);
+}
+
 void rehash_ip() {
   /* cache our ip on load instead of every 30 seconds */
   char *ip4 = NULL, *ip6 = NULL;
@@ -577,6 +590,33 @@ void rehash_ip() {
     struct bot_addr *bi = (struct bot_addr *) get_user(&USERENTRY_BOTADDR, conf.bot->u);
     listen_all(bi->telnet_port, 0);
     my_port = bi->telnet_port;
+  } else if (conf.bot->localhub) {
+    // If not listening on the domain socket, open it up
+    bool listening = 0;
+    for (int i = 0; i < dcc_total; i++) {
+      if (dcc[i].type && (dcc[i].type == &DCC_TELNET) && (!strcmp(dcc[i].host, conf.localhub_socket))) {
+        listening = 1;
+        break;
+      }
+    }
+    if (!listening) {
+      // Listen on the unix domain socket
+      port_t port;
+      int i = open_listen_addr_by_af(conf.localhub_socket, &port, AF_UNIX);
+      if (i < 0) {
+        putlog(LOG_ERRORS, "*", "Can't listen on %s - %s", conf.localhub_socket, i == -1 ? "it's taken." : "couldn't assign file.");
+      } else {
+        /* now setup dcc entry */
+        int idx = new_dcc(&DCC_TELNET, 0);
+        dcc[idx].addr = 0L;
+        strlcpy(dcc[idx].host, conf.localhub_socket, sizeof(dcc[idx].host));
+        dcc[idx].port = 0;
+        dcc[idx].sock = i;
+        dcc[idx].timeval = now;
+        strlcpy(dcc[idx].nick, "(unix_domain)", NICKLEN);
+        putlog(LOG_DEBUG, "*", "Listening on telnet %s", conf.localhub_socket);
+      }
+    }
   }
 }
 
@@ -609,6 +649,11 @@ void chanprog()
 
   add_myself_to_userlist();
 
+  if (conf.bot->localhub)
+    add_child_bots();
+  else if (!conf.bot->hub)
+    add_localhub();
+
   rehash_ip();
 
   /* set our shell info */
@@ -651,6 +696,11 @@ void reload()
   /* make sure I am added and conf.bot->u is set */
   add_myself_to_userlist();
 
+  if (conf.bot->localhub)
+    add_child_bots();
+  else if (!conf.bot->hub)
+    add_localhub();
+
   /* Make sure no removed users/bots are still connected. */
   check_stale_dcc_users();
 

+ 1 - 0
src/chanprog.h

@@ -28,6 +28,7 @@ bool bot_shouldjoin(struct userrec* , struct flag_record *, struct chanset_t *,
 bool shouldjoin(struct chanset_t *);
 char *samechans(const char *, const char *);
 void add_myself_to_userlist();
+void add_child_bots();
 bool is_hub(const char*);
 void load_internal_users();
 void setup_HQ(int);

+ 1 - 5
src/cmds.c

@@ -3131,11 +3131,7 @@ static void cmd_newleaf(int idx, char *par)
     userlist = adduser(userlist, handle, "none", "-", USER_OP, 1);
     u1 = get_user_by_handle(userlist, handle);
     bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
-
-    bi->uplink = (char *) my_calloc(1, strlen(conf.bot->nick) + 1); 
-/*      strcpy(bi->uplink, conf.bot->nick); */
-    bi->uplink[0] = 0;
-
+    bi->uplink = (char *) my_calloc(1, 1);
     bi->address = (char *) my_calloc(1, 1);
     bi->telnet_port = 3333;
     bi->relay_port = 3333;

+ 20 - 11
src/conf.c

@@ -359,6 +359,17 @@ void conf_checkpids(conf_bot *bots, bool all)
       bot->pid = checkpid(bot->nick, bot);
 }
 
+static char* datafile(const char* prefix, const char* nick) {
+  static char buf[DIRMAX] = "";
+  char *tmpnick = NULL, *tmp_ptr = NULL;
+
+  tmpnick = tmp_ptr = strdup(nick);
+  strtolower(tmpnick);
+  simple_snprintf(buf, sizeof buf, STR("%s/.%s.%s"), conf.datadir, prefix, tmpnick);
+  free(tmp_ptr);
+  return buf;
+}
+
 /*
  * Return the PID of a bot if it is running, otherwise return 0
  */
@@ -367,23 +378,19 @@ pid_t
 checkpid(const char *nick, conf_bot *bot)
 {
   FILE *f = NULL;
-  char buf[DIRMAX] = "", *tmpnick = NULL, *tmp_ptr = NULL;
   pid_t pid = 0;
 
-  tmpnick = tmp_ptr = strdup(nick);
 
-  strtolower(tmpnick);
-
-  simple_snprintf(buf, sizeof buf, STR("%s/.pid.%s"), conf.datadir, tmpnick);
-  free(tmp_ptr);
+  char buf[DIRMAX] = "";
+  char *bufp = datafile("pid", nick);
 
   if (bot && !(bot->pid_file))
-    bot->pid_file = strdup(buf);
-  else if (bot && strcasecmp(bot->pid_file, buf))
-    str_redup(&bot->pid_file, buf);
+    bot->pid_file = strdup(bufp);
+  else if (bot && strcasecmp(bot->pid_file, bufp))
+    str_redup(&bot->pid_file, bufp);
 
-  if ((f = fopen(buf, "r"))) {
-    char *bufp = NULL, *pids = NULL;
+  if ((f = fopen(bufp, "r"))) {
+    char *pids = NULL;
 
     if (!fgets(buf, sizeof(buf), f)) {
       fclose(f);
@@ -484,6 +491,8 @@ conf_addbot(const char *nick, const char *ip, const char *host, const char *ip6)
   if (!conf.localhub && !bot->hub && !bot->disabled) {
     bot->localhub = 1;          /* first bot */
     conf.localhub = strdup(bot->nick);
+
+    conf.localhub_socket = strdup(datafile("sock", conf.localhub));
   }
 
   list_append((struct list_type **) &(conf.bots), (struct list_type *) bot);

+ 1 - 0
src/conf.h

@@ -34,6 +34,7 @@ typedef struct conf_b {
   int uid;
   int autocron;         /* should the bot auto crontab itself? */
   char *localhub;	/* my localhub */
+  char *localhub_socket;	/* my localhub unix socket */
   char *datadir;
   char *username;       /* shell username */
   char *homedir;        /* homedir */

+ 104 - 53
src/dcc.c

@@ -222,9 +222,15 @@ greet_new_bot(int idx)
   dcc[idx].u.bot->version[0] = 0;
   dcc[idx].u.bot->sysname[0] = 0;
   dcc[idx].u.bot->numver = 0;
-  if (conf.bot->hub && dcc[idx].user && (!(dcc[idx].user->flags & USER_OP))) {
-    putlog(LOG_BOTS, "*", "Rejecting link from %s", dcc[idx].nick);
-    dprintf(idx, "error You are being rejected.\n");
+  // Reject -o bots, and if we're a localhub who hasnt linked to hub yet, dont allow links in
+  if ((conf.bot->hub || conf.bot->localhub) && dcc[idx].user && (!(dcc[idx].user->flags & USER_OP) || (conf.bot->localhub && (dcc[idx].status & STAT_UNIXDOMAIN) && !have_linked_to_hub))) {
+    if (!(dcc[idx].user->flags & USER_OP)) {
+      putlog(LOG_BOTS, "*", "Rejecting link from %s", dcc[idx].nick);
+      dprintf(idx, "error You are being rejected.\n");
+    } else if (conf.bot->localhub && !have_linked_to_hub) {
+      putlog(LOG_BOTS, "*", "Delaying link from %s", dcc[idx].nick);
+      dprintf(idx, "error Delaying your link until I get userfile.\n");
+    }
     dprintf(idx, "bye\n");
     killsock(dcc[idx].sock);
     lostdcc(idx);
@@ -300,20 +306,28 @@ bot_version(int idx, char *par)
   if (par[0])
     vversion = newsplit(&par);
 
-  if (conf.bot->hub) {
+  if (conf.bot->hub || (conf.bot->localhub && (dcc[idx].status & STAT_UNIXDOMAIN))) {
     putlog(LOG_BOTS, "*", "Linked to %s.\n", dcc[idx].nick);
     chatout("*** Linked to %s.\n", dcc[idx].nick);
+  } else {
+    putlog(LOG_BOTS, "*", "Linked to botnet.");
+    chatout("*** Linked to botnet.\n");
+  }
 
+  if (conf.bot->hub || conf.bot->localhub) {
     if (bot_hublevel(dcc[idx].user) < 999) {
-      if (!bot_aggressive_to(dcc[idx].user))    //not aggressive, so they are technically my uplink.
+      if (!bot_aggressive_to(dcc[idx].user)) {   //not aggressive, so they are technically my uplink.
         uplink_idx = idx;
+        // This is now done in share_endstartup
+        //have_linked_to_hub = 1;
+      }
       dcc[idx].hub = 1;
     }
 
     botnet_send_nlinked(idx, dcc[idx].nick, conf.bot->nick, '!', vlocalhub, vbuildts, vcommit, vversion);
   } else {
-    putlog(LOG_BOTS, "*", "Linked to botnet.");
-    chatout("*** Linked to botnet.\n");
+        // This is now done in share_endstartup
+        //have_linked_to_hub = 1;
     uplink_idx = idx;
     dcc[idx].hub = 1;
   }
@@ -347,7 +361,8 @@ failed_link(int idx)
   }
   strlcpy(s, dcc[idx].nick, sizeof(s));
   lostdcc(idx);
-  autolink_cycle(s);            /* Check for more auto-connections */
+  if (conf.bot->hub || conf.bot->localhub)
+    autolink_cycle(s);            /* Check for more auto-connections */
 }
 
 static void
@@ -538,6 +553,7 @@ display_dcc_bot(int idx, char *buf, size_t bufsiz)
   buf[i++] = b_status(idx) & STAT_OFFEREDU ? 'B' : 'b';
   buf[i++] = b_status(idx) & STAT_SENDINGU ? 'D' : 'd';
   buf[i++] = b_status(idx) & STAT_GETTINGU ? 'E' : 'e';
+  buf[i++] = b_status(idx) & STAT_UNIXDOMAIN ? 'Z' : 'z';
 #ifdef USE_IPV6
   if (sockprotocol(dcc[idx].sock) == AF_INET6 && dcc[idx].host6[0])
     buf[i++] = '6';
@@ -984,10 +1000,13 @@ dcc_chat_pass(int idx, char *buf, int atr)
     } else if (!strcasecmp(pass, STR("neg."))) {		/* we're done, link up! */
       dcc[idx].type = &DCC_BOT_NEW;
       dcc[idx].u.bot = (struct bot_info *) my_calloc(1, sizeof(struct bot_info));
-      dcc[idx].status = STAT_CALLED;
+      if (dcc[idx].status & STAT_UNIXDOMAIN)
+        dcc[idx].status = STAT_UNIXDOMAIN|STAT_CALLED;
+      else
+        dcc[idx].status = STAT_CALLED;
       dprintf(idx, "goodbye!\n");
       greet_new_bot(idx);
-      if (conf.bot->hub)
+      if (conf.bot->hub || conf.bot->localhub)
         send_timesync(idx);
     } else if (!strcasecmp(pass, STR("neg"))) {
       int snum = findanysnum(dcc[idx].sock);
@@ -1371,6 +1390,8 @@ dcc_telnet(int idx, char *buf, int ii)
   in_addr_t ip;
   port_t port;
   char s[UHOSTLEN + 1] = "";
+  int i;
+  char x[1024] = "";
 
   if (dcc_total + 1 > max_dcc) {
     int j;
@@ -1395,6 +1416,24 @@ dcc_telnet(int idx, char *buf, int ii)
   /* Buffer data received on this socket.  */
   sockoptions(sock, EGG_OPTION_SET, SOCK_BUFFER);
 
+  int af_type = sockprotocol(sock);
+
+  if (af_type == AF_UNIX) {
+
+    //i = new_dcc(&DCC_IDENT, 0);
+    i = new_dcc(&DCC_TELNET_ID, 0);
+
+    simple_snprintf(x, sizeof(x), "UNKNOWN@localhost");
+    dcc[i].sock = sock;
+    dcc[i].uint.ident_sock = dcc[idx].sock;
+    dcc[i].port = 0;
+    dcc[i].timeval = now;
+    strlcpy(dcc[i].nick, "*", NICKLEN);
+    putlog(LOG_BOTS, "*", "Connection over local socket: %s", s);
+    dcc_telnet_got_ident(i, x);
+    return;
+  }
+
 #if SIZEOF_SHORT == 2
   if (port < 1024) {
 #else
@@ -1405,9 +1444,6 @@ dcc_telnet(int idx, char *buf, int ii)
     return;
   }
 
-  char x[1024] = "";
-  int i;
-
   putlog(LOG_DEBUG, "*", "Telnet connection: %s/%d", s, port);
 
   // Are they ignored by IP?
@@ -1431,7 +1467,7 @@ dcc_telnet(int idx, char *buf, int ii)
   dcc[i].user = get_user_by_host(x);		/* check for matching -telnet!telnet@ip */
   strlcpy(dcc[i].host, s, UHOSTLEN);
 #ifdef USE_IPV6
-  if (sockprotocol(sock) == AF_INET6)
+  if (af_type == AF_INET6)
     strlcpy(dcc[i].host6, s, sizeof(dcc[i].host6));
 #endif /* USE_IPV6 */
   dcc[i].port = port;
@@ -1738,6 +1774,8 @@ dcc_telnet_id(int idx, char *buf, int atr)
   }
   correct_handle(nick);
   strlcpy(dcc[idx].nick, nick, NICKLEN);
+  if (!strcmp(dcc[idx].host, "UNKNOWN@localhost"))
+    simple_snprintf(dcc[idx].host, sizeof(dcc[idx].host), "%s@localhost", nick);
   if (dcc[idx].user->bot) {
     if (!strcasecmp(conf.bot->nick, dcc[idx].nick)) {
       putlog(LOG_BOTS, "*", "Refused telnet connection from %s (tried using my botnetnick)", dcc[idx].host);
@@ -1757,7 +1795,7 @@ dcc_telnet_id(int idx, char *buf, int atr)
   } else {
   }
 
-  if (dcc[idx].bot) {
+  if (dcc[idx].bot && !(dcc[idx].status & STAT_UNIXDOMAIN)) {
     char shost[UHOSTLEN + 20] = "", sip[UHOSTLEN + 20] = "", user[30] = "";
     simple_snprintf(shost, sizeof(shost), "-telnet!%s", dcc[idx].host);
     char *p = strchr(dcc[idx].host, '@');
@@ -1825,7 +1863,7 @@ dcc_telnet_pass(int idx, int atr)
     strlcpy(dcc[idx].u.chat->con_chan, chanset ? chanset->dname : "*", sizeof(dummy.con_chan));
   }
 
-  if (conf.bot->hub) {
+  if (conf.bot->hub || (conf.bot->localhub && (dcc[idx].status & STAT_UNIXDOMAIN))) {
     if (dcc[idx].bot) {
       /* negotiate a new linking scheme */
       int i = 0;
@@ -1852,7 +1890,10 @@ dcc_telnet_pass(int idx, int atr)
 static void
 eof_dcc_telnet_id(int idx)
 {
-  putlog(LOG_MISC, "*", "Lost telnet connection to %s/%d", dcc[idx].host, dcc[idx].port);
+  if (dcc[idx].port)
+    putlog(LOG_MISC, "*", "Lost telnet connection to %s/%d", dcc[idx].host, dcc[idx].port);
+  else
+    putlog(LOG_MISC, "*", "Lost local connection for: %s", dcc[idx].host);
   killsock(dcc[idx].sock);
   lostdcc(idx);
 }
@@ -2034,49 +2075,56 @@ dcc_telnet_got_ident(int i, char *host)
 
   strlcpy(dcc[i].host, host, UHOSTLEN);
 
-  char shost[UHOSTLEN + 20] = "", sip[UHOSTLEN + 20] = "";
-  char *p = strchr(host, '@');
-  *p = 0;
+  bool unix_domain = 0;
+  if (!strcmp(host, "UNKNOWN@localhost"))
+    unix_domain = 1;
 
-  simple_snprintf(shost, sizeof(shost), "-telnet!%s", dcc[i].host);
-  simple_snprintf(sip, sizeof(sip), "-telnet!%s@%s", host, iptostr(htonl(dcc[i].addr)));
 
-  if (match_ignore(shost) || match_ignore(sip)) {
-    putlog(LOG_DEBUG, "*", "Ignored telnet connection from: %s[%s]",dcc[i].host, iptostr(htonl(dcc[i].addr)));
-    killsock(dcc[i].sock);
-    lostdcc(i);
-    return;
-  }
-  
-  if (protect_telnet) {
-    struct userrec *u = NULL;
-    bool ok = 1;
+  if (!unix_domain) {
+    char shost[UHOSTLEN + 20] = "", sip[UHOSTLEN + 20] = "";
+    char *p = strchr(host, '@');
+    *p = 0;
 
-    u = dcc[i].user;
-    if (!u)
-      u = get_user_by_host(sip);			/* Check for -telnet!ident@ip */
-    if (!u)
-      u = get_user_by_host(shost);		/* Check for -telnet!ident@host */
-    if (!u)
-      ok = 0;
+    simple_snprintf(shost, sizeof(shost), "-telnet!%s", dcc[i].host);
+    simple_snprintf(sip, sizeof(sip), "-telnet!%s@%s", host, iptostr(htonl(dcc[i].addr)));
 
-    if (ok && u && conf.bot->hub && !(u->flags & USER_HUBA))
-      ok = 0;
-    /* if I am a chanhub and they dont have +c then drop */
-    if (ok && (!conf.bot->hub && ischanhub() && u && !(u->flags & USER_CHUBA)))
-      ok = 0;
-/*    else if (!(u->flags & USER_PARTY))
-    ok = 0; */
-    if (!ok && u && u->bot)
-      ok = 1;
-    if (!ok && (dcc[idx].status & LSTN_PUBLIC))
-      ok = 1;
-    if (!ok) {
-      putlog(LOG_MISC, "*", "Denied telnet: %s, No Access", dcc[i].host);
+    if (match_ignore(shost) || match_ignore(sip)) {
+      putlog(LOG_DEBUG, "*", "Ignored telnet connection from: %s[%s]",dcc[i].host, iptostr(htonl(dcc[i].addr)));
       killsock(dcc[i].sock);
       lostdcc(i);
       return;
     }
+
+    if (protect_telnet) {
+      struct userrec *u = NULL;
+      bool ok = 1;
+
+      u = dcc[i].user;
+      if (!u)
+        u = get_user_by_host(sip);			/* Check for -telnet!ident@ip */
+      if (!u)
+        u = get_user_by_host(shost);		/* Check for -telnet!ident@host */
+      if (!u)
+        ok = 0;
+
+      if (ok && u && conf.bot->hub && !(u->flags & USER_HUBA))
+        ok = 0;
+      /* if I am a chanhub and they dont have +c then drop */
+      if (ok && (!conf.bot->hub && ischanhub() && u && !(u->flags & USER_CHUBA)))
+        ok = 0;
+  /*    else if (!(u->flags & USER_PARTY))
+      ok = 0; */
+      if (!ok && u && u->bot)
+        ok = 1;
+      if (!ok && (dcc[idx].status & LSTN_PUBLIC))
+        ok = 1;
+      if (!ok) {
+        putlog(LOG_MISC, "*", "Denied telnet: %s, No Access", dcc[i].host);
+        killsock(dcc[i].sock);
+        lostdcc(i);
+        return;
+      }
+    }
   }
 
   /* Do not buffer data anymore. All received and stored data is passed
@@ -2088,6 +2136,9 @@ dcc_telnet_got_ident(int i, char *host)
   /* Copy acceptable-nick/host mask */
   dcc[i].status = (STAT_TELNET | STAT_ECHO | STAT_COLOR | STAT_BANNER | STAT_CHANNELS | STAT_BOTS | STAT_WHOM);
 
+  if (unix_domain)
+    dcc[i].status |= STAT_UNIXDOMAIN;
+
   /* Copy acceptable-nick/host mask */
   strlcpy(dcc[i].nick, dcc[idx].host, HANDLEN);
   dcc[i].timeval = now;
@@ -2096,7 +2147,7 @@ dcc_telnet_got_ident(int i, char *host)
   /* This is so we dont tell someone doing a portscan anything
    * about ourselves. <cybah>
    */
-  if (conf.bot->hub)
+  if (conf.bot->hub || (conf.bot->localhub && unix_domain))
     dprintf(i, " \n");			/* represents hub that support new linking scheme */
   else
     dprintf(i, "%s\n", response(RES_USERNAME));

+ 1 - 0
src/dcc.h

@@ -174,6 +174,7 @@ struct dupwait_info {
 #define STAT_CHANNELS BIT10  /* show channels on login? */
 #define STAT_BOTS    BIT11   /* Show bots linked on login? */
 #define STAT_WHOM    BIT12   /* show .whom on login? */
+#define STAT_UNIXDOMAIN  BIT13	  /* This dcc is over a unix domain */
 
 /* For stripping out mIRC codes
  */

+ 1 - 1
src/dccutil.c

@@ -64,7 +64,7 @@ static int dcc_flood_thr = 3;
 void
 init_dcc()
 {
-  if (!conf.bot->hub)
+  if (!conf.bot->hub || conf.bot->localhub)
     protect_telnet = 0;
   if (max_dcc < 1)
     max_dcc = 1;

+ 4 - 1
src/enclink.c

@@ -57,7 +57,10 @@ static void ghost_link_case(int idx, direction_t direction)
 
       bzero(&sa, socklen);
       getsockname(socklist[snum].sock, (struct sockaddr *) &sa, &socklen);
-      port = sa.sin_port;
+      if (sa.sin_family == AF_UNIX)
+        port = 0;
+      else
+        port = sa.sin_port;
     }
 
     /* initkey-gen */

+ 6 - 2
src/main.c

@@ -98,6 +98,7 @@ bool	used_B = 0;		/* did we get started with -B? */
 int 	role;
 bool 	loading = 0;
 int	default_flags = 0;	/* Default user flags and */
+bool     have_linked_to_hub = 0;  /* Have we ever been linked to a hub? */
 int	default_uflags = 0;	/* Default userdefinied flags for people
 				   who say 'hello' or for .adduser */
 int     do_restart = 0;
@@ -191,6 +192,9 @@ void fatal(const char *s, int recoverable)
   if (my_port)
     listen_all(my_port, 1); /* close the listening port... */
 
+  if (conf.bot && conf.bot->localhub)
+    unlink(conf.localhub_socket);
+
   sdprintf(STR("Closing %d sockets"), dcc_total);
   for (int i = 0; i < dcc_total; i++) {
     if (dcc[i].type && dcc[i].sock >= 0) {
@@ -517,7 +521,7 @@ static void core_secondly()
       do_fork();
   ++cnt;
 
-  if ((cnt % 30) == 0) {
+  if (((conf.bot->localhub || conf.bot->hub) && (cnt % 30) == 0) || (cnt % 5) == 0) {
     autolink_cycle(NULL);         /* attempt autolinks */
     cnt = 0;
   }
@@ -591,7 +595,7 @@ static void core_minutely()
       fatal(STR("MEMORY HACKED"), 0);
     check_maxfiles();
     check_mypid();
-  } else
+  } else if (conf.bot->hub || conf.bot->localhub)
     send_timesync(-1);
 
   check_bind_time(&nowtm);

+ 1 - 1
src/main.h

@@ -15,7 +15,7 @@ enum {
 
 extern int		role, default_flags, default_uflags, do_confedit,
 			updating, do_restart;
-extern bool		use_stderr, backgrd, used_B, term_z, loading, restart_was_update, restarting;
+extern bool		use_stderr, backgrd, used_B, term_z, loading, have_linked_to_hub, restart_was_update, restarting;
 extern char		tempdir[], *binname, owner[], version[151], ver[101], quit_msg[], *socksfile;
 extern time_t		online_since, now, restart_time;
 extern uid_t		myuid;

+ 7 - 0
src/misc.c

@@ -992,6 +992,13 @@ int updatebin(int idx, char *par, int secs)
 
 int bot_aggressive_to(struct userrec *u)
 {
+  if (conf.bot->localhub) {
+    for (conf_bot* bot = conf.bots; bot && bot->nick; bot = bot->next) {
+      if (!bot->hub && !strcmp(u->handle, bot->nick))
+        return 1;
+    }
+  }
+
   char mypval[HANDLEN + 4] = "", botpval[HANDLEN + 4] = "";
 
   link_pref_val(u, botpval);

+ 7 - 3
src/mod/server.mod/server.c

@@ -945,11 +945,15 @@ static void dcc_chat_hostresolved(int i)
 #else
   dcc[i].sock = getsock(0);
 #endif /* USE_IPV6 */
-  if (dcc[i].sock < 0 || open_telnet_dcc(dcc[i].sock, ip, buf) < 0) {
+  int open_telnet_return = 0;
+  if (dcc[i].sock < 0 || (open_telnet_return = open_telnet_dcc(dcc[i].sock, ip, buf)) < 0) {
+    if (open_telnet_return == -1)
+      dcc[i].sock = -1;
     strlcpy(buf, strerror(errno), sizeof(buf));
     putlog(LOG_MISC, "*", "%s: CHAT (%s!%s)", "DCC connection failed", dcc[i].nick, dcc[i].host);
     putlog(LOG_MISC, "*", "    (%s)", buf);
-    killsock(dcc[i].sock);
+    if (dcc[i].sock != -1)
+      killsock(dcc[i].sock);
     lostdcc(i);
   } else {
     bool ok = 1;
@@ -977,7 +981,7 @@ static void dcc_chat_hostresolved(int i)
 static void server_secondly()
 {
   if (cycle_time)
-    cycle_time--;
+    --cycle_time;
   deq_msg();
   if (!resolvserv && serv < 0 && !trying_server)
     connect_server();

+ 22 - 7
src/mod/share.mod/share.c

@@ -998,9 +998,10 @@ share_ufsend(int idx, char *par)
 #else
     sock = getsock(SOCK_BINARY);        /* Don't buffer this -> mark binary. */
 #endif /* USE_IPV6 */
-    if (sock < 0 || open_telnet_dcc(sock, ip, port) < 0) {
+    int open_telnet_return = 0;
+    if (sock < 0 || (open_telnet_return = open_telnet_dcc(sock, ip, port)) < 0) {
       fclose(f);
-      if (sock != -1)
+      if (open_telnet_return != -1 && sock != -1)
         killsock(sock);
       putlog(LOG_BOTS, "@", "Asynchronous connection failed!");
       dprintf(idx, "s e Can't connect to you!\n");
@@ -1058,10 +1059,15 @@ static void
 share_endstartup(int idx, char *par)
 {
   dcc[idx].status &= ~STAT_GETTING;
+  // Share any local changes out
+  dump_resync(idx);
   /* Send to any other sharebots */
-  if (conf.bot->hub)
+  if (conf.bot->hub || conf.bot->localhub) {
+    have_linked_to_hub = 1;
     hook_read_userfile();
-  else {
+  }
+
+  if (!conf.bot->hub) {
     /* Our hostmask may have been updated on connect, but the new userfile may not have it. */
     check_hostmask();
   }
@@ -1086,6 +1092,10 @@ static void share_userfile_line(int idx, char *par) {
 
 static void share_userfile_start(int idx, char *par) {
   dcc[idx].status |= STAT_GETTING;
+  /* Start up a tbuf to queue outgoing changes for this bot until the
+   * userlist is done transferring.
+   */
+  new_tbuf(dcc[idx].nick);
   stream_in = new bd::Stream();
 }
 
@@ -1178,11 +1188,13 @@ shareout_prot(struct userrec *u, const char *format, ...)
     s[2 + (l = 509)] = 0;
   va_end(va);
 
+  int localhub = nextbot(u->handle);
+
   for (int i = 0; i < dcc_total; i++) {
     if (dcc[i].type && (dcc[i].type->flags & DCT_BOT) && 
        (dcc[i].status & STAT_SHARE) && !(dcc[i].status & (STAT_GETTING | STAT_SENDING)) &&
-       /* only send to hubs and to the same user */
-       (dcc[i].hub || dcc[i].user == u)) {
+       /* only send to hubs, the bot itself, or the localhub in the chain */
+       (dcc[i].hub || dcc[i].user == u || (localhub != -1 && i == localhub))) {
       tputs(dcc[i].sock, s, l + 2);
     }
   }
@@ -1406,7 +1418,10 @@ static void share_read_stream(int idx, bd::Stream& stream) {
   /* The userfile we received may just be bogus or missing important users */
   load_internal_users();
   add_myself_to_userlist();
-  
+
+  if (conf.bot->localhub)
+    add_child_bots();
+
   /* Make sure no removed users/bots are still connected. */
   check_stale_dcc_users();
 

+ 8 - 4
src/mod/update.mod/update.c

@@ -132,8 +132,10 @@ static void update_ufsend(int idx, char *par)
 #else
     sock = getsock(SOCK_BINARY); /* Don't buffer this -> mark binary. */
 #endif /* USE_IPV6 */
-    if (sock < 0 || open_telnet_dcc(sock, ip, port) < 0) {
-      killsock(sock);
+    int open_telnet_return = 0;
+    if (sock < 0 || (open_telnet_return = open_telnet_dcc(sock, ip, port)) < 0) {
+      if (open_telnet_return != -1 && sock != -1)
+        killsock(sock);
       putlog(LOG_BOTS, "*", "Asynchronous connection failed!");
       dprintf(idx, "sb e Can't connect to you!\n");
       zapfbot(idx);
@@ -196,7 +198,9 @@ static void got_nu(char *botnick, char *code, char *par)
     if (!conf.bot->u || !userlist || !get_user_by_handle(userlist, botnick))	/* probably still getting userfile */
       return;
 
-    if (tandbot && tandbot->bot && !strcmp(tandbot->bot, botnick)) /* dont listen to our uplink.. use normal upate system.. */
+    if (uplink_idx == -1) return; // No uplink?
+
+    if (!strcmp(dcc[uplink_idx].nick, botnick)) /* dont listen to our uplink.. use normal upate system.. */
       return;
   }
 
@@ -209,7 +213,7 @@ static void got_nu(char *botnick, char *code, char *par)
      if (!conf.bot->hub) {
        dont_restructure = 1;
        putlog(LOG_MISC, "*", "Linking to %s for binary update.", botnick);
-       botunlink(-2, tandbot->bot, "Restructure for update.");
+       botunlink(-2, dcc[uplink_idx].nick, "Restructure for update.");
        usleep(1000 * 500);
        botlink("", -3, botnick);
      } else 

+ 97 - 44
src/net.c

@@ -565,23 +565,32 @@ int open_telnet_raw(int sock, const char *ipIn, port_t sport, bool proxy_on, int
     port = sport;
   }
 
-  /* figure out which ip to bind to locally (v4 or v6) based on what the host ip is .. */
-  if ((is_resolved = is_dotted_ip(ip))) {	/* already resolved */
-  
-    /* bind to our cached ip for v4/v6 depending on what the ip is */
-    initialize_sockaddr(is_resolved, NULL, 0, &so);
+  socklen_t socklen;
+  if (sport) {
+    /* figure out which ip to bind to locally (v4 or v6) based on what the host ip is .. */
+    if ((is_resolved = is_dotted_ip(ip))) {	/* already resolved */
+
+      /* bind to our cached ip for v4/v6 depending on what the ip is */
+      initialize_sockaddr(is_resolved, NULL, 0, &so);
+
+      if (bind(sock, &so.sa, SIZEOF_SOCKADDR(so)) < 0) {
+        putlog(LOG_DEBUG, "*", "Failed to bind to socket %d: %s", sock, strerror(errno));
+        killsock(sock);
+        return -1;
+      }
 
-    if (bind(sock, &so.sa, SIZEOF_SOCKADDR(so)) < 0) {
-      putlog(LOG_DEBUG, "*", "Failed to bind to socket %d: %s", sock, strerror(errno));
+      /* initialize so for connect using the host/port */
+      initialize_sockaddr(is_resolved, ip, port, &so);
+    } else {	/* if not resolved, resolve it with blocking calls.. (shouldn't happen ever) */
+      sdprintf("WARNING: open_telnet_raw(%s,%d) was passed an unresolved hostname.", ip, port);
       killsock(sock);
       return -1;
     }
-
-    /* initialize so for connect using the host/port */
-    initialize_sockaddr(is_resolved, ip, port, &so);
-  } else {	/* if not resolved, resolve it with blocking calls.. (shouldn't happen ever) */
-    sdprintf("WARNING: open_telnet_raw(%s,%d) was passed an unresolved hostname.", ip, port);
-    return -1;
+    socklen = SIZEOF_SOCKADDR(so);
+  } else { // Unix domain socket
+    so.sock_un.sun_family = AF_UNIX;
+    strcpy(so.sock_un.sun_path, ip);
+    socklen = SUN_LEN(&so.sock_un);
   }
 
   for (int i = 0; i < MAXSOCKS; i++) {
@@ -593,13 +602,13 @@ int open_telnet_raw(int sock, const char *ipIn, port_t sport, bool proxy_on, int
     }
   }
 
-  if (identd)
+  if (identd && sport) //Only open identd if not a unix domain socket
     identd_open(myipstr(is_resolved), ipIn, identd);
 
   int rc = -1;
 
   /* make the connect attempt */
-  rc = connect(sock, &so.sa, SIZEOF_SOCKADDR(so));
+  rc = connect(sock, (struct sockaddr *)&so.sa, socklen);
 
   if (rc < 0) {    
     if (errno == EINPROGRESS) {
@@ -645,9 +654,9 @@ int open_telnet(const char *ip, port_t port, bool proxy, int identd)
  * 'addr' is ignored if af_def is AF_INET6 -poptix (02/03/03)
  */
 #ifdef USE_IPV6
-int open_address_listen(in_addr_t addr, int af_def, port_t *port)
+int open_address_listen(const char* ip, int af_def, port_t *port)
 #else
-int open_address_listen(in_addr_t addr, port_t *port)
+int open_address_listen(const char* ip, port_t *port)
 #endif /* USE_IPV6 */
  {
 //  if (firewall[0]) {
@@ -658,7 +667,7 @@ int open_address_listen(in_addr_t addr, port_t *port)
 
   int sock = 0;
   socklen_t addrlen;
-  struct sockaddr_in name;
+  union sockaddr_union name;
 
 #ifdef USE_IPV6
   if (af_def == AF_INET6) {
@@ -695,7 +704,7 @@ int open_address_listen(in_addr_t addr, port_t *port)
       return -1;
     }
   } else {
-    sock = getsock(SOCK_LISTEN, AF_INET);
+    sock = getsock(SOCK_LISTEN, af_def);
 #else
     sock = getsock(SOCK_LISTEN);
 #endif /* USE_IPV6 */
@@ -703,28 +712,48 @@ int open_address_listen(in_addr_t addr, port_t *port)
     if (sock < 0)
       return -1;
 
-    debug2("Opening listen socket on port %d with AF_INET, sock: %d", *port, sock);
-    bzero((char *) &name, sizeof(struct sockaddr_in));
-    name.sin_family = AF_INET;
-    name.sin_port = htons(*port); /* 0 = just assign us a port */
-    name.sin_addr.s_addr = addr;
-    if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
-      if (!(identd_hack && *port == 113))
-        putlog(LOG_DEBUG, "*", "Failed to bind to socket %d for listen on port %d: %s", sock, *port, strerror(errno));
-      killsock(sock);
-      return -1;
+    if (af_def == AF_UNIX)
+      debug2("Opening listen socket on %s, sock: %d", ip, sock);
+    else
+      debug3("Opening listen socket on %s:%d with AF_INET, sock: %d", ip, *port, sock);
+
+    bzero((char *) &name, sizeof(struct sockaddr *));
+    if (af_def == AF_UNIX) {
+      name.sock_un.sun_family = AF_UNIX;
+      strcpy(name.sock_un.sun_path, ip);
+      unlink(name.sock_un.sun_path);
+      addrlen = SUN_LEN(&name.sock_un);
+    } else {
+      name.sin.sin_family = af_def;
+      name.sin.sin_port = htons(*port); /* 0 = just assign us a port */
+      name.sin.sin_addr.s_addr = inet_addr(ip);
+      addrlen = sizeof(struct sockaddr_in);
     }
-    /* what port are we on? */
-    addrlen = sizeof(name);
-    if (getsockname(sock, (struct sockaddr *) &name, &addrlen) < 0) {
-      if (!(identd_hack && *port == 113))
-        putlog(LOG_DEBUG, "*", "Failed to getsockname on socket %d for listen on port %d: %s", sock, *port, strerror(errno));
+
+    if (bind(sock, (struct sockaddr *) &name, addrlen) < 0) {
+      if (!(identd_hack && *port == 113)) {
+        if (af_def == AF_UNIX)
+          putlog(LOG_DEBUG, "*", "Failed to bind to socket %d for listen on %s: %s", sock, ip, strerror(errno));
+        else
+          putlog(LOG_DEBUG, "*", "Failed to bind to socket %d for listen on %s:%d: %s", sock, ip, *port, strerror(errno));
+      }
       killsock(sock);
       return -1;
     }
-    *port = ntohs(name.sin_port);
+
+    if (af_def != AF_UNIX) {
+      /* what port are we on? */
+      if (getsockname(sock, (struct sockaddr *) &name, &addrlen) < 0) {
+        if (!(identd_hack && *port == 113))
+          putlog(LOG_DEBUG, "*", "Failed to getsockname on socket %d for listen on port %d: %s", sock, *port, strerror(errno));
+        killsock(sock);
+        return -1;
+      }
+      *port = ntohs(name.sin.sin_port);
+    }
+
     if (listen(sock, 1) < 0) {
-      if (!(identd_hack && *port == 113))
+      if (!(identd_hack && *port == 113) && af_def != AF_UNIX)
         putlog(LOG_DEBUG, "*", "Failed to listen on socket %d for on port %d: %s", sock, *port, strerror(errno));
       killsock(sock);
       return -1;
@@ -733,7 +762,10 @@ int open_address_listen(in_addr_t addr, port_t *port)
   }
 #endif /* USE_IPV6 */
 
-  debug2("Opened listen socket on port %d with AF_INET, sock: %d", *port, sock);
+  if (af_def == AF_UNIX)
+    debug2("Opened listen socket on %s, sock: %d", ip, sock);
+  else
+    debug3("Opened listen socket on %s:%d with AF_INET, sock: %d", ip, *port, sock);
 
   return sock;
 }
@@ -744,9 +776,9 @@ int open_address_listen(in_addr_t addr, port_t *port)
 int open_listen(port_t *port)
 {
 #ifdef USE_IPV6
-  return open_address_listen(getmyip(), AF_INET, port);
+  return open_address_listen(iptostr(getmyip()), AF_INET, port);
 #else
-  return open_address_listen(getmyip(), port);
+  return open_address_listen(iptostr(getmyip()), port);
 #endif /* USE_IPV6 */
 }
 
@@ -757,7 +789,18 @@ int open_listen(port_t *port)
 int open_listen_by_af(port_t *port, int af_def)
 {
 #ifdef USE_IPV6
-  return open_address_listen(getmyip(), af_def, port);
+  return open_address_listen(iptostr(getmyip()), af_def, port);
+#else
+  return -1;
+#endif /* USE_IPV6 */
+}
+
+int open_listen_addr_by_af(const char *ip, port_t *port, int af_def)
+{
+  if (!ip)
+    ip = iptostr(getmyip());
+#ifdef USE_IPV6
+  return open_address_listen(ip, af_def, port);
 #else
   return -1;
 #endif /* USE_IPV6 */
@@ -825,9 +868,19 @@ int answer(int sock, char *caller, in_addr_t *ip, port_t *port, int binary)
       *ip = ntohl(*ip);
     } else {
 #endif /* USE_IPV6 */
-      *ip = from.sin_addr.s_addr;
-      strlcpy(caller, iptostr(*ip), 121);
-      *ip = ntohl(*ip);
+      if (af_ty == AF_UNIX) {
+        struct sockaddr_un sock_un;
+        socklen_t socklen = sizeof(sock_un);
+
+        bzero(&sock_un, socklen);
+        getsockname(sock, (struct sockaddr*) &sock_un, &socklen);
+        strcpy(caller, sock_un.sun_path);
+        *port = 0;
+      } else {
+        *ip = from.sin_addr.s_addr;
+        strlcpy(caller, iptostr(*ip), 121);
+        *ip = ntohl(*ip);
+      }
 #ifdef USE_IPV6 
     }
 #endif /* USE_IPV6 */
@@ -836,7 +889,7 @@ int answer(int sock, char *caller, in_addr_t *ip, port_t *port, int binary)
 #ifdef USE_IPV6
       if (af_ty == AF_INET6)
         *port = ntohs(from6.sin6_port);
-      else
+      else if (af_ty == AF_INET)
 #endif /* USE_IPV6 */
         *port = ntohs(from.sin_port);
     }

+ 5 - 2
src/net.h

@@ -8,6 +8,7 @@
 #include "types.h"
 #include <netinet/in.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <setjmp.h>
 
 namespace bd {
@@ -66,6 +67,7 @@ union sockaddr_union {			/* replaced by sockname_t */
 #ifdef USE_IPV6
   struct sockaddr_in6 sin6;
 #endif /* USE_IPV6 */
+  struct sockaddr_un sock_un;
 };
 
 /* This is used by the net module to keep track of sockets and what's
@@ -117,10 +119,11 @@ int answer(int, char *, in_addr_t *, port_t *, int);
 int findanysnum(register int);
 int open_listen(port_t *);
 int open_listen_by_af(port_t *, int);
+int open_listen_addr_by_af(const char*, port_t *, int);
 #ifdef USE_IPV6
-int open_address_listen(in_addr_t, int, port_t *);
+int open_address_listen(const char*, int, port_t *);
 #else
-int open_address_listen(in_addr_t, port_t *);
+int open_address_listen(const char*, port_t *);
 #endif /* USE_IPV6 */
 int open_telnet(const char *, port_t, bool proxy = 0, int identd = 0);
 int open_telnet_dcc(int, char *, char *);

+ 12 - 3
src/set.c

@@ -625,7 +625,7 @@ void var_userfile_share_line(char *line, int idx, bool share)
   set_noshare = 1;
   var_set(var, NULL, line);
   /* leaf bots don't need to bother attempting to share; there are no bots linked to us! */
-  if (share && conf.bot->hub)
+  if (share && (conf.bot->hub || conf.bot->localhub))
     botnet_send_var_broad(idx, var);
   set_noshare = 0;
 }
@@ -831,8 +831,17 @@ void write_vars_and_cmdpass(bd::Stream& stream, int idx)
   int i = 0;
 
   for (i = 0; vars[i].name; i++) {
-    /* send blanks if our variable isn't set, theirs MIGHT be set and needs to be UNSET */
-    stream << buf.printf("@ %s %s\n", vars[i].name, vars[i].gdata ? vars[i].gdata : "");
+    // If we're a localhub, dont share variables set to not leaf default unless we're linked to a hub.
+    // Otherwise, we share out servers, and the child bots connect to the default list,
+    // before ever receiving the actual list from the hub. (Along with stuff like realname)
+    if (conf.bot->hub ||
+        (conf.bot->localhub &&
+         (!(vars[i].flags & VAR_NOLDEF) ||
+          ((vars[i].flags & VAR_NOLDEF) && have_linked_to_hub))
+         )) {
+      /* send blanks if our variable isn't set, theirs MIGHT be set and needs to be UNSET */
+      stream << buf.printf("@ %s %s\n", vars[i].name, vars[i].gdata ? vars[i].gdata : "");
+    }
   }
 
   for (struct cmd_pass *cp = cmdpass; cp; cp = cp->next)

+ 9 - 6
src/userent.c

@@ -37,6 +37,7 @@
 #include "dccutil.h"
 #include "crypt.h"
 #include "botmsg.h"
+#include "botnet.h"
 #include <bdlib/src/Stream.h>
 #include <bdlib/src/String.h>
 
@@ -95,8 +96,9 @@ bool def_kill(struct user_entry *e)
 
 void write_userfile_protected(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
-  /* only write if saving local, or if sending to hub, or if sending to same user as entry */
-  if (idx == -1 || dcc[idx].hub || dcc[idx].user == u) {
+  int localhub = nextbot(u->handle);
+  /* only write if saving local, or if sending to hub, or if sending to same user as entry, or the localhub in the chain */
+  if (idx == -1 || dcc[idx].hub || dcc[idx].user == u || (localhub != -1 && idx == localhub)) {
     bd::String buf;
     stream << buf.printf("--%s %s\n", e->type->name, e->u.string);
   }
@@ -299,7 +301,7 @@ static bool set_unpack(struct userrec *u, struct user_entry *e)
   head = curr = e->u.list;
   e->u.extra = NULL;
 
-  if (conf.bot->hub || !strcasecmp(conf.bot->nick, u->handle)) {
+  if (conf.bot->hub || conf.bot->localhub || !strcasecmp(conf.bot->nick, u->handle)) {
     struct xtra_key *t = NULL;
     char *key = NULL, *data = NULL;
 
@@ -351,7 +353,7 @@ static bool set_gotshare(struct userrec *u, struct user_entry *e, char *buf, int
   /* var_set_by_name() called set_user(), no need to do it again... */
   } 
   /* not else if as the hub might have gotten a botset for itself */
-  if (conf.bot->hub) {
+  if (conf.bot->hub || conf.bot->localhub) {
   /* only hubs need to bother saving this stuff, leaf bots just store it in vars[] */
     struct xtra_key *xk = (struct xtra_key *) my_calloc(1, sizeof(struct xtra_key));
 
@@ -364,8 +366,9 @@ static bool set_gotshare(struct userrec *u, struct user_entry *e, char *buf, int
 
 static void set_write_userfile(bd::Stream& stream, const struct userrec *u, const struct user_entry *e, int idx)
 {
-  /* only write if saving local, or if sending to hub, or if sending to same user as entry */
-  if (idx == -1 || dcc[idx].hub || dcc[idx].user == u) {
+  int localhub = nextbot(u->handle);
+  /* only write if saving local, or if sending to hub, or if sending to same user as entry, or the localhub in the chain */
+  if (idx == -1 || dcc[idx].hub || dcc[idx].user == u || (localhub != -1 && idx == localhub)) {
     struct xtra_key *x = (struct xtra_key *) e->u.extra;
     bd::String buf;
 

+ 4 - 1
src/userrec.c

@@ -92,6 +92,9 @@ struct userrec *host_conflicts(char *host)
 
   for (u = userlist; u; u = u->next) {
     for (q = (struct list_type *) get_user(&USERENTRY_HOSTS, u); q; q = q->next) {
+      // Ignore -telnet!*@* hosts for this check, as this is for irc.
+      if (!strncmp(q->extra, "-telnet!", 8))
+        continue;
       if (wild_match(host, q->extra) || wild_match(q->extra, host))
         return u;
     }
@@ -388,7 +391,7 @@ static void write_user(const struct userrec *u, bd::Stream& stream, int idx)
     } else
 #endif
     if (ue->type)
-      if (conf.bot->hub)
+      if (conf.bot->hub || conf.bot->localhub)
         ue->type->write_userfile(stream, u, ue, idx);
   }
 }

+ 16 - 3
src/users.c

@@ -1142,7 +1142,7 @@ void autolink_cycle_leaf(char *start)
    if (dcc[i].type) {
     if ((dcc[i].type == &DCC_BOT_NEW) || (dcc[i].type == &DCC_FORK_BOT))
       return;
-    if (dcc[i].type == &DCC_BOT) {
+    if (dcc[i].hub && dcc[i].type == &DCC_BOT) {
       strlcpy(curhub, dcc[i].nick, sizeof(curhub));
       break;
     }
@@ -1199,8 +1199,21 @@ void autolink_cycle(char *start)
 {
   if (conf.bot->hub)
     autolink_cycle_hub(start);
-  else
+  else if (conf.bot->localhub)
     autolink_cycle_leaf(start);
+  else { //Connect to the localhub
+    if (tands == 0) {
+      // Make sure not already trying for the localhub
+      for (int i = 0; i < dcc_total; i++) {
+       if (dcc[i].type) {
+        if ((dcc[i].type == &DCC_BOT_NEW) || (dcc[i].type == &DCC_FORK_BOT))
+          return;
+       }
+      }
+      sdprintf("need to link to my localhub: %s\n", conf.localhub);
+      botlink("", -3, conf.localhub);
+    }
+  }
 }
 
 
@@ -1210,7 +1223,7 @@ void check_stale_dcc_users()
     if (!dcc[i].type || !dcc[i].nick[0]) continue;
     
 
-    if (dcc[i].user == NULL) { /* Removed user */
+    if (dcc[i].user == NULL && !(dcc[i].user = get_user_by_handle(userlist, dcc[i].nick))) { /* Removed user */
       if (dcc[i].type == &DCC_BOT || dcc[i].type == &DCC_FORK_BOT || dcc[i].type == &DCC_BOT_NEW)
         botunlink(i, dcc[i].nick, "No longer a valid bot.");
       else if (dcc[i].type == &DCC_CHAT) {