Bladeren bron

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 jaren geleden
bovenliggende
commit
dbd12a770c
25 gewijzigde bestanden met toevoegingen van 525 en 216 verwijderingen
  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) {