]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/inspsocket.cpp
Mode +b stuff, probably wont work yet
[user/henk/code/inspircd.git] / src / inspsocket.cpp
index 8177a31827b3d67b8aa2833a01a50984bd2a32c0..4264700f54bd02cb6a7fe2c8942758cc2b7369ce 100644 (file)
  * ---------------------------------------------------
  */
 
-#include "inspircd_config.h"
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
 #include <string>
-#include <unistd.h>
-#include <fcntl.h>
-#include <poll.h>
 #include <sstream>
 #include <iostream>
 #include <fstream>
 #include <stdexcept>
+#include "inspircd_config.h"
 #include "socket.h"
 #include "inspircd.h"
 #include "configreader.h"
 #include "inspstring.h"
 #include "helperfuncs.h"
 #include "socketengine.h"
+#include "message.h"
 
 
 extern InspIRCd* ServerInstance;
@@ -49,14 +42,17 @@ InspSocket::InspSocket()
        this->ClosePending = false;
 }
 
-InspSocket::InspSocket(int newfd, char* ip)
+InspSocket::InspSocket(int newfd, const char* ip)
 {
        this->fd = newfd;
        this->state = I_CONNECTED;
        strlcpy(this->IP,ip,MAXBUF);
        this->ClosePending = false;
-       ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
-       socket_ref[this->fd] = this;
+       if (this->fd > -1)
+       {
+               ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
+               socket_ref[this->fd] = this;
+       }
 }
 
 InspSocket::InspSocket(const std::string &ahost, int aport, bool listening, unsigned long maxtime) : fd(-1)
@@ -88,8 +84,11 @@ InspSocket::InspSocket(const std::string &ahost, int aport, bool listening, unsi
                        else
                        {
                                this->state = I_LISTENING;
-                               ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
-                               socket_ref[this->fd] = this;
+                               if (this->fd > -1)
+                               {
+                                       ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
+                                       socket_ref[this->fd] = this;
+                               }
                                log(DEBUG,"New socket now in I_LISTENING state");
                                return;
                        }
@@ -121,6 +120,23 @@ InspSocket::InspSocket(const std::string &ahost, int aport, bool listening, unsi
        }
 }
 
+void InspSocket::WantWrite()
+{
+       /** XXX:
+        * The socket engine may only have each FD in the list ONCE.
+        * This means we cant watch for write AND read at the same
+        * time. We have to remove the READ fd, to insert the WRITE
+        * fd. Once we receive our WRITE event (which WILL ARRIVE,
+        * pretty much gauranteed) we switch back to watching for
+        * READ events again.
+        *
+        * This behaviour may be fixed in a later version.
+        */
+       this->WaitingForWriteEvent = true;
+       ServerInstance->SE->DelFd(this->fd);
+       ServerInstance->SE->AddFd(this->fd,false,X_ESTAB_MODULE);
+}
+
 void InspSocket::SetQueues(int nfd)
 {
        // attempt to increase socket sendq and recvq as high as its possible
@@ -159,6 +175,69 @@ bool InspSocket::DoResolve()
        return true;
 }
 
+/* Most irc servers require you to specify the ip you want to bind to.
+ * If you dont specify an IP, they rather dumbly bind to the first IP
+ * of the box (e.g. INADDR_ANY). In InspIRCd, we scan thought the IP
+ * addresses we've bound server ports to, and we try and bind our outbound
+ * connections to the first usable non-loopback and non-any IP we find.
+ * This is easier to configure when you have a lot of links and a lot
+ * of servers to configure.
+ */
+bool InspSocket::BindAddr()
+{
+       insp_inaddr n;
+       ConfigReader Conf;
+
+       log(DEBUG,"In InspSocket::BindAddr()");
+       for (int j =0; j < Conf.Enumerate("bind"); j++)
+       {
+               std::string Type = Conf.ReadValue("bind","type",j);
+               std::string IP = Conf.ReadValue("bind","address",j);
+               if (Type == "servers")
+               {
+                       if ((IP != "*") && (IP != "127.0.0.1") && (IP != ""))
+                       {
+                               insp_sockaddr s;
+                               char resolved_addr[MAXBUF];
+                               
+                               if (!inet_aton(IP.c_str(),&n))
+                               {
+                                       /* If they gave a hostname, bind to the IP it resolves to */
+                                       log(DEBUG,"Resolving host %s",IP.c_str());
+                                       if (CleanAndResolve(resolved_addr, IP.c_str(), true))
+                                       {
+                                               log(DEBUG,"Resolved host %s to %s",IP.c_str(),resolved_addr);
+                                               IP = resolved_addr;
+                                       }
+                               }
+
+                               if (inet_aton(IP.c_str(),&n))
+                               {
+                                       log(DEBUG,"Found an IP to bind to: %s",IP.c_str());
+                                       s.sin_addr = n;
+                                       s.sin_family = AF_INET;
+                                       if (bind(this->fd,(struct sockaddr*)&s,sizeof(s)) < 0)
+                                       {
+                                               log(DEBUG,"Cant bind()");
+                                               this->state = I_ERROR;
+                                               this->OnError(I_ERR_BIND);
+                                               this->fd = -1;
+                                               return false;
+                                       }
+                                       log(DEBUG,"bind() reports outbound fd bound to ip %s",IP.c_str());
+                                       return true;
+                               }
+                               else
+                               {
+                                       log(DEBUG,"Address '%s' was not an IP address",IP.c_str());
+                               }
+                       }
+               }
+       }
+       log(DEBUG,"Found no suitable IPs to bind, binding INADDR_ANY");
+       return true;
+}
+
 bool InspSocket::DoConnect()
 {
        log(DEBUG,"In DoConnect()");
@@ -171,6 +250,9 @@ bool InspSocket::DoConnect()
                return false;
        }
 
+       if (!this->BindAddr())
+               return false;
+
        log(DEBUG,"Part 2 DoConnect() %s",this->IP);
        inet_aton(this->IP,&addy);
        addr.sin_family = AF_INET;
@@ -195,9 +277,12 @@ bool InspSocket::DoConnect()
                }
        }
        this->state = I_CONNECTING;
-       ServerInstance->SE->AddFd(this->fd,false,X_ESTAB_MODULE);
-       socket_ref[this->fd] = this;
-       this->SetQueues(this->fd);
+       if (this->fd > -1)
+       {
+               ServerInstance->SE->AddFd(this->fd,false,X_ESTAB_MODULE);
+               socket_ref[this->fd] = this;
+               this->SetQueues(this->fd);
+       }
        log(DEBUG,"Returning true from InspSocket::DoConnect");
        return true;
 }
@@ -363,8 +448,11 @@ bool InspSocket::Poll()
                        /* Our socket was in write-state, so delete it and re-add it
                         * in read-state.
                         */
-                       ServerInstance->SE->DelFd(this->fd);
-                       ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
+                       if (this->fd > -1)
+                       {
+                               ServerInstance->SE->DelFd(this->fd);
+                               ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
+                       }
                        return this->OnConnected();
                break;
                case I_LISTENING:
@@ -375,7 +463,20 @@ bool InspSocket::Poll()
                        return true;
                break;
                case I_CONNECTED:
-                       n = this->OnDataReady();
+
+                       if (this->WaitingForWriteEvent)
+                       {
+                               /* Switch back to read events */
+                               ServerInstance->SE->DelFd(this->fd);
+                               ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
+                               /* Trigger the write event */
+                               n = this->OnWriteReady();
+                       }
+                       else
+                       {
+                               /* Process the read event */
+                               n = this->OnDataReady();
+                       }
                        /* Flush any pending, but not till after theyre done with the event
                         * so there are less write calls involved.
                         * Both FlushWriteBuffer AND the return result of OnDataReady must
@@ -412,6 +513,7 @@ void InspSocket::OnError(InspSocketError e) { return; }
 int InspSocket::OnDisconnect() { return 0; }
 int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; }
 bool InspSocket::OnDataReady() { return true; }
+bool InspSocket::OnWriteReady() { return true; }
 void InspSocket::OnTimeout() { return; }
 void InspSocket::OnClose() { return; }