Forward port SOCKSENDBUF, SOCKRECVBUF features from 2.10.11.
[ircu2.10.12-pk.git] / ircd / s_bsd.c
index ed5bc8399de23f8b952bf4670631aeab3aa2ac64..27d66901f0418abc6de1a29c78445a1bcca98b61 100644 (file)
 #include "channel.h"
 #include "class.h"
 #include "hash.h"
+#include "ircd_alloc.h"
 #include "ircd_log.h"
 #include "ircd_features.h"
 #include "ircd_osdep.h"
 #include "ircd_reply.h"
+#include "ircd_snprintf.h"
 #include "ircd_string.h"
 #include "ircd.h"
 #include "list.h"
@@ -49,7 +51,6 @@
 #include "s_misc.h"
 #include "s_user.h"
 #include "send.h"
-#include "sprintf_irc.h"
 #include "struct.h"
 #include "support.h"
 #include "sys.h"
@@ -176,13 +177,15 @@ void report_error(const char* text, const char* who, int err)
  * a non-null pointer, otherwise reply will be null.
  * if successful start the connection, otherwise notify opers
  */
-static void connect_dns_callback(void* vptr, struct DNSReply* reply)
+static void connect_dns_callback(void* vptr, struct hostent* hp)
 {
   struct ConfItem* aconf = (struct ConfItem*) vptr;
+  assert(aconf);
   aconf->dns_pending = 0;
-  if (reply) {
-    memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr));
-    connect_server(aconf, 0, reply);
+  if (hp) {
+    memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+    MyFree(hp);
+    connect_server(aconf, 0);
   }
   else
     sendto_opmask_butone(0, SNO_OLDSNO, "Connect to %s failed: host lookup",
@@ -287,7 +290,7 @@ static int connect_inet(struct ConfItem* aconf, struct Client* cptr)
   /*
    * we want a big buffer for server connections
    */
-  if (!os_set_sockbufs(cli_fd(cptr), SERVER_TCP_WINDOW)) {
+  if (!os_set_sockbufs(cli_fd(cptr), feature_int(FEAT_SOCKSENDBUF), feature_int(FEAT_SOCKRECVBUF))) {
     cli_error(cptr) = errno;
     report_error(SETBUFS_ERROR_MSG, cli_name(cptr), errno);
     close(cli_fd(cptr));
@@ -356,7 +359,7 @@ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf)
 
   switch (os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written)) {
   case IO_SUCCESS:
-    cli_flags(cptr) &= ~FLAGS_BLOCKED;
+    ClrFlag(cptr, FLAG_BLOCKED);
 
     cli_sendB(cptr) += bytes_written;
     cli_sendB(&me)  += bytes_written;
@@ -373,14 +376,14 @@ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf)
      * say it was blocked
      */
     if (bytes_written < bytes_count)
-      cli_flags(cptr) |= FLAGS_BLOCKED;
+      SetFlag(cptr, FLAG_BLOCKED);
     break;
   case IO_BLOCKED:
-    cli_flags(cptr) |= FLAGS_BLOCKED;
+    SetFlag(cptr, FLAG_BLOCKED);
     break;
   case IO_FAILURE:
     cli_error(cptr) = errno;
-    cli_flags(cptr) |= FLAGS_DEADSOCKET;
+    SetFlag(cptr, FLAG_DEADSOCKET);
     break;
   }
   return bytes_written;
@@ -393,8 +396,7 @@ void release_dns_reply(struct Client* cptr)
   assert(MyConnect(cptr));
 
   if (cli_dns_reply(cptr)) {
-    assert(0 < cli_dns_reply(cptr)->ref_count);
-    --(cli_dns_reply(cptr))->ref_count;
+    MyFree(cli_dns_reply(cptr));
     cli_dns_reply(cptr) = 0;
   }
 }
@@ -458,7 +460,7 @@ static int completed_connection(struct Client* cptr)
    * Make us timeout after twice the timeout for DNS look ups
    */
   cli_lasttime(cptr) = CurrentTime;
-  cli_flags(cptr) |= FLAGS_PINGSENT;
+  SetFlag(cptr, FLAG_PINGSENT);
 
   sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s :%s",
                 cli_name(&me), cli_serv(&me)->timestamp, newts,
@@ -538,7 +540,7 @@ void close_connection(struct Client *cptr)
     socket_del(&(cli_socket(cptr))); /* queue a socket delete */
     cli_fd(cptr) = -1;
   }
-  cli_flags(cptr) |= FLAGS_DEADSOCKET;
+  SetFlag(cptr, FLAG_DEADSOCKET);
 
   MsgQClear(&(cli_sendQ(cptr)));
   client_drop_sendq(cli_connect(cptr));
@@ -608,17 +610,28 @@ void add_connection(struct Listener* listener, int fd) {
     close(fd);
     return;
   }
+  /*
+   * Disable IP (*not* TCP) options.  In particular, this makes it impossible
+   * to use source routing to connect to the server.  If we didn't do this
+   * (and if intermediate networks didn't drop source-routed packets), an
+   * attacker could successfully IP spoof us...and even return the anti-spoof
+   * ping, because the options would cause the packet to be routed back to
+   * the spoofer's machine.  When we disable the IP options, we delete the
+   * source route, and the normal routing takes over.
+   */
+  os_disable_options(fd);
 
   /*
    * Add this local client to the IPcheck registry.
    *
    * If they're throttled, murder them, but tell them why first.
    */
-  if (!IPcheck_local_connect(addr.sin_addr, &next_target) && !listener->server) {
+  if (!IPcheck_local_connect(addr.sin_addr, &next_target) && !listener->server)
+  {
     ++ServerStats->is_ref;
-     write(fd, throttle_message, strlen(throttle_message));
-     close(fd);
-     return;
+    write(fd, throttle_message, strlen(throttle_message));
+    close(fd);
+    return;
   }
 
   new_client = make_client(0, ((listener->server) ? 
@@ -628,6 +641,7 @@ void add_connection(struct Listener* listener, int fd) {
    * Copy ascii address to 'sockhost' just in case. Then we have something
    * valid to put into error messages...  
    */
+  SetIPChecked(new_client);
   ircd_ntoa_r(cli_sock_ip(new_client), (const char*) &addr.sin_addr);   
   strcpy(cli_sockhost(new_client), cli_sock_ip(new_client));
   (cli_ip(new_client)).s_addr = addr.sin_addr.s_addr;
@@ -690,18 +704,21 @@ static int read_packet(struct Client *cptr, int socket_ready)
        DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) {
     switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) {
     case IO_SUCCESS:
-      if (length) {
-        cli_lasttime(cptr) = CurrentTime;
+      if (length)
+      {
+        if (!IsServer(cptr))
+          cli_lasttime(cptr) = CurrentTime;
         if (cli_lasttime(cptr) > cli_since(cptr))
           cli_since(cptr) = cli_lasttime(cptr);
-        cli_flags(cptr) &= ~(FLAGS_PINGSENT | FLAGS_NONL);
+        ClrFlag(cptr, FLAG_PINGSENT);
+        ClrFlag(cptr, FLAG_NONL);
       }
       break;
     case IO_BLOCKED:
       break;
     case IO_FAILURE:
       cli_error(cptr) = errno;
-      /* cptr->flags |= FLAGS_DEADSOCKET; */
+      /* SetFlag(cpt, FLAG_DEADSOCKET); */
       return 0;
     }
   }
@@ -710,37 +727,26 @@ static int read_packet(struct Client *cptr, int socket_ready)
    * For server connections, we process as many as we can without
    * worrying about the time of day or anything :)
    */
-  if (length > 0 && IsServer(cptr)) {
+  if (length > 0 && IsServer(cptr))
     return server_dopacket(cptr, readbuf, length);
-  }
-  else {
+  else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr)))
+    return connect_dopacket(cptr, readbuf, length);
+  else
+  {
     /*
      * Before we even think of parsing what we just read, stick
      * it on the end of the receive queue and do it when its
      * turn comes around.
      */
-    if (length > 0 && 0 == dbuf_put(&(cli_recvQ(cptr)), readbuf, length)) {
+    if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0)
       return exit_client(cptr, cptr, &me, "dbuf_put fail");
-    }
 
-    /*
-     * XXX - cptr will always be a user or unregistered
-     */
-    if (IsUser(cptr) &&
-       DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))
+    if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))
       return exit_client(cptr, cptr, &me, "Excess Flood");
 
     while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && 
            (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10))
     {
-      /*
-       * If it has become registered as a Server
-       * then skip the per-message parsing below.
-       */
-      if (IsServer(cptr)) {
-        dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf));
-        return (dolen) ? server_dopacket(cptr, readbuf, dolen) : 1;
-      }
       dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE);
       /*
        * Devious looking...whats it do ? well..if a client
@@ -750,19 +756,46 @@ static int read_packet(struct Client *cptr, int socket_ready)
        * deletes the rest of the buffer contents.
        * -avalon
        */
-      if (0 == dolen) {
+      if (dolen == 0)
+      {
         if (DBufLength(&(cli_recvQ(cptr))) < 510)
-          cli_flags(cptr) |= FLAGS_NONL;
+          SetFlag(cptr, FLAG_NONL);
         else
           DBufClear(&(cli_recvQ(cptr)));
       }
-      else if (CPTR_KILLED == client_dopacket(cptr, dolen))
+      else if (client_dopacket(cptr, dolen) == CPTR_KILLED)
         return CPTR_KILLED;
+      /*
+       * If it has become registered as a Server
+       * then skip the per-message parsing below.
+       */
+      if (IsHandshake(cptr) || IsServer(cptr))
+      {
+        while (-1)
+        {
+          dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf));
+          if (dolen <= 0)
+            return 1;
+          else if (dolen == 0)
+          {
+            if (DBufLength(&(cli_recvQ(cptr))) < 510)
+              SetFlag(cptr, FLAG_NONL);
+            else
+              DBufClear(&(cli_recvQ(cptr)));
+          }
+          else if ((IsServer(cptr) &&
+                    server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) ||
+                   (!IsServer(cptr) &&
+                    connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED))
+            return CPTR_KILLED;
+        }
+      }
     }
 
     /* If there's still data to process, wait 2 seconds first */
     if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
-       !(cli_freeflag(cptr) & FREEFLAG_TIMER)) {
+       !t_onqueue(&(cli_proc(cptr))))
+    {
       Debug((DEBUG_LIST, "Adding client process timer for %C", cptr));
       cli_freeflag(cptr) |= FREEFLAG_TIMER;
       timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr),
@@ -785,8 +818,7 @@ static int read_packet(struct Client *cptr, int socket_ready)
  * be done, we loose the information about who started the connection and
  * it's considered an auto connect.
  */
-int connect_server(struct ConfItem* aconf, struct Client* by,
-                   struct DNSReply* reply)
+int connect_server(struct ConfItem* aconf, struct Client* by)
 {
   struct Client*   cptr = 0;
   assert(0 != aconf);
@@ -822,9 +854,8 @@ int connect_server(struct ConfItem* aconf, struct Client* by,
    * not a ip# string, then try and find the appropriate host record.
    */
   if (INADDR_NONE == aconf->ipnum.s_addr) {
-    char buf[HOSTLEN + 1];
-    assert(0 == reply);
     if (INADDR_NONE == (aconf->ipnum.s_addr = inet_addr(aconf->host))) {
+      char buf[HOSTLEN + 1];
       struct DNSQuery  query;
 
       query.vptr     = aconf;
@@ -832,19 +863,12 @@ int connect_server(struct ConfItem* aconf, struct Client* by,
       host_from_uh(buf, aconf->host, HOSTLEN);
       buf[HOSTLEN] = '\0';
 
-      reply = gethost_byname(buf, &query);
-
-      if (!reply) {
-        aconf->dns_pending = 1;
-        return 0;
-      }
-      memcpy(&aconf->ipnum, reply->hp->h_addr, sizeof(struct in_addr));
+      gethost_byname(buf, &query);
+      aconf->dns_pending = 1;
     }
+    return 0;
   }
   cptr = make_client(NULL, STAT_UNKNOWN_SERVER);
-  if (reply)
-    ++reply->ref_count;
-  cli_dns_reply(cptr) = reply;
 
   /*
    * Copy these in so we have something for error detection.
@@ -890,7 +914,8 @@ int connect_server(struct ConfItem* aconf, struct Client* by,
    */
   make_server(cptr);
   if (by && IsUser(by)) {
-    sprintf_irc(cli_serv(cptr)->by, "%s%s", NumNick(by));
+    ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s",
+                 NumNick(by));
     assert(0 == cli_serv(cptr)->user);
     cli_serv(cptr)->user = cli_user(by);
     cli_user(by)->refcnt++;
@@ -958,7 +983,7 @@ static void client_sock_callback(struct Event* ev)
   assert(0 != ev_socket(ev));
   assert(0 != s_data(ev_socket(ev)));
 
-  con = s_data(ev_socket(ev));
+  con = (struct Connection*) s_data(ev_socket(ev));
 
   assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);
 
@@ -990,7 +1015,7 @@ static void client_sock_callback(struct Event* ev)
   case ET_EOF: /* end of file on socket */
     Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr),
           cli_error(cptr)));
-    cli_flags(cptr) |= FLAGS_DEADSOCKET;
+    SetFlag(cptr, FLAG_DEADSOCKET);
     if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) {
       exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
                      cli_name(cptr), cli_serv(cptr)->last_error_msg);
@@ -1002,7 +1027,7 @@ static void client_sock_callback(struct Event* ev)
     break;
 
   case ET_WRITE: /* socket is writable */
-    cli_flags(cptr) &= ~FLAGS_BLOCKED;
+    ClrFlag(cptr, FLAG_BLOCKED);
     if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048)
       list_next_channels(cptr, 64);
     Debug((DEBUG_SEND, "Sending queued data to %C", cptr));
@@ -1046,7 +1071,7 @@ static void client_timer_callback(struct Event* ev)
   assert(0 != t_data(ev_timer(ev)));
   assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev));
 
-  con = t_data(ev_timer(ev));
+  con = (struct Connection*) t_data(ev_timer(ev));
 
   assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);
 
@@ -1054,9 +1079,9 @@ static void client_timer_callback(struct Event* ev)
 
   assert(0 == cptr || con == cli_connect(cptr));
 
-  con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */
-
   if (ev_type(ev)== ET_DESTROY) {
+    con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */
+
     if (!con_freeflag(con) && !cptr)
       free_connection(con); /* client is being destroyed */
   } else {