]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/connection.cpp
Fixes for nonblocking socket issues
[user/henk/code/inspircd.git] / src / connection.cpp
1 #include <connection.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <sys/errno.h>
5 #include <sys/ioctl.h>
6 #include <sys/utsname.h>
7 #include <vector>
8 #include <string>
9 #include <deque>
10 #include "inspircd.h"
11 #include "modules.h"
12
13 using namespace std;
14
15 extern std::vector<Module*> modules;
16 extern std::vector<ircd_module*> factory;
17
18 extern int MODCOUNT;
19
20
21 packet::packet()
22 {
23         srand(time(NULL));
24         id = random();
25 }
26
27 packet::~packet()
28 {
29 }
30
31 connection::connection()
32 {
33         key = GenKey();
34         fd = 0;
35 }
36
37
38 bool connection::CreateListener(char* host, int p)
39 {
40         sockaddr_in host_address;
41         int flags;
42         in_addr addy;
43         int on = 0;
44         struct linger linger = { 0 };
45         
46         fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
47         if (fd <= 0)
48         {
49                 return false;
50         }
51
52         memset((void*)&host_address, 0, sizeof(host_address));
53
54         host_address.sin_family = AF_INET;
55
56         if (!strcmp(host,""))
57         {
58                 host_address.sin_addr.s_addr = htonl(INADDR_ANY);
59         }
60         else
61         {
62                 inet_aton(host,&addy);
63                 host_address.sin_addr = addy;
64         }
65
66         host_address.sin_port = htons(p);
67
68         if (bind(fd,(sockaddr*)&host_address,sizeof(host_address))<0)
69         {
70                 return false;
71         }
72
73         // make the socket non-blocking
74         flags = fcntl(fd, F_GETFL, 0);
75         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
76
77         this->port = p;
78
79         setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&on,sizeof(on));
80         linger.l_onoff = 1;
81         linger.l_linger = 0;
82         setsockopt(fd,SOL_SOCKET,SO_LINGER,(const char*)&linger,sizeof(linger));
83         
84         // attempt to increase socket sendq and recvq as high as its possible
85         // to get them on linux.
86         int sendbuf = 32768;
87         int recvbuf = 32768;
88         setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
89         setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
90         
91         listen(this->fd,5);
92
93         return true;
94 }
95
96 bool ircd_connector::SetHostAddress(char* host, int port)
97 {
98         memset((void*)&addr, 0, sizeof(addr));
99         addr.sin_family = AF_INET;
100         inet_aton(host,&addr.sin_addr);
101         addr.sin_port = htons(port);
102         return true;
103 }
104
105 bool ircd_connector::MakeOutboundConnection(char* host, int port)
106 {
107         hostent* hoste = gethostbyname(host);
108         if (!hoste)
109         {
110                 WriteOpers("Failed to look up hostname for %s, using as an ip address",host);
111                 this->SetHostAddress(host,port);
112         }
113         else
114         {
115                 WriteOpers("Found hostname for %s",host);
116                 this->SetHostAddress(hoste->h_addr,port);
117         }
118
119         this->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
120         if (this->fd >= 0)
121         {
122                 if(connect(this->fd, (sockaddr*)&addr,sizeof(addr)))
123                 {
124                         WriteOpers("connect() failed for %s",host);
125                         return false;
126                 }
127                 int flags = fcntl(this->fd, F_GETFL, 0);
128                 fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
129                 int sendbuf = 32768;
130                 int recvbuf = 32768;
131                 setsockopt(this->fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
132                 setsockopt(this->fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
133                 return true;
134         }
135         else
136         {
137                 WriteOpers("socket() failed!");
138         }
139
140         return false;
141 }
142
143
144 bool connection::BeginLink(char* targethost, int port, char* password, char* servername)
145 {
146         char connect[MAXBUF];
147         
148         ircd_connector connector;
149         
150         if (this->fd)
151         {
152                 if (connector.MakeOutboundConnection(targethost,port))
153                 {
154                         // targethost has been turned into an ip...
155                         // we dont want this as the server name.
156                         connector.SetServerName(servername);
157                         sprintf(connect,"S %s %s :%s",getservername().c_str(),password,getserverdesc().c_str());
158                         connector.SetState(STATE_NOAUTH_OUTBOUND);
159                         this->connectors.push_back(connector);
160                         return this->SendPacket(connect, servername);
161                 }
162                 else
163                 {
164                         WriteOpers("Could not create outbound connection to %s:%d",targethost,port);
165                 }
166         }
167         return false;
168 }
169
170 bool connection::AddIncoming(int fd,char* targethost)
171 {
172         char connect[MAXBUF];
173         
174         ircd_connector connector;
175         
176         // targethost has been turned into an ip...
177         // we dont want this as the server name.
178         connector.SetServerName(targethost);
179         connector.SetDescriptor(fd);
180         connector.SetState(STATE_NOAUTH_INBOUND);
181         int flags = fcntl(fd, F_GETFL, 0);
182         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
183         int sendbuf = 32768;
184         int recvbuf = 32768;
185         setsockopt(fd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf)); 
186         setsockopt(fd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
187         this->connectors.push_back(connector);
188         return true;
189 }
190
191 void connection::TerminateLink(char* targethost)
192 {
193         // this locates the targethost in the connection::connectors vector of the class,
194         // and terminates it by sending it an SQUIT token and closing its descriptor.
195         // TerminateLink with a null string causes a terminate of ALL links
196 }
197
198
199 // Returns a pointer to the connector for 'host'
200 ircd_connector* connection::FindHost(std::string host)
201 {
202         for (int i = 0; i < this->connectors.size(); i++)
203         {
204                 if (this->connectors[i].GetServerName() == host)
205                 {
206                         return &this->connectors[i];
207                 }
208         }
209         return NULL;
210 }
211
212 std::string ircd_connector::GetServerName()
213 {
214         return this->servername;
215 }
216
217 void ircd_connector::SetServerName(std::string serv)
218 {
219         this->servername = serv;
220 }
221
222
223 int ircd_connector::GetDescriptor()
224 {
225         return this->fd;
226 }
227
228 int ircd_connector::GetState()
229 {
230         return this->state;
231 }
232
233
234 void ircd_connector::SetState(int state)
235 {
236         this->state = state;
237 }
238
239 void ircd_connector::SetDescriptor(int fd)
240 {
241         this->fd = fd;
242 }
243
244 bool connection::SendPacket(char *message, const char* host)
245 {
246         ircd_connector* cn = this->FindHost(host);
247         
248         if (cn)
249         {
250                 log(DEBUG,"main: Connection::SendPacket() sent '%s' to %s",message,cn->GetServerName().c_str());
251
252                 strncat(message,"\n",MAXBUF);
253                 // returns false if the packet could not be sent (e.g. target host down)
254                 if (send(cn->GetDescriptor(),message,strlen(message),0)<0)
255                 {
256                         log(DEBUG,"send() failed for Connection::SendPacket(): %s",strerror(errno));
257                         return false;
258                 }
259                 return true;
260         }
261 }
262
263 // receives a packet from any where there is data waiting, first come, first served
264 // fills the message and host values with the host where the data came from.
265
266 bool connection::RecvPacket(std::deque<std::string> &messages, char* host)
267 {
268         char data[32767];
269         memset(data, 0, 32767);
270         for (int i = 0; i < this->connectors.size(); i++)
271         {
272                 // returns false if the packet could not be sent (e.g. target host down)
273                 int rcvsize = 0;
274                 if (rcvsize = recv(this->connectors[i].GetDescriptor(),data,32767,0))
275                 {
276                         if (rcvsize > 0)
277                         {
278                                 char* l = strtok(data,"\n");
279                                 while (l)
280                                 {
281                                         char sanitized[32767];
282                                         memset(sanitized, 0, 32767);
283                                         int ptt = 0;
284                                         for (int pt = 0; pt < strlen(l); pt++)
285                                         {
286                                                 if (l[pt] != '\r')
287                                                 {
288                                                         sanitized[ptt++] = l[pt];
289                                                 }
290                                         }
291                                         sanitized[ptt] = '\0';
292                                         if (strlen(sanitized))
293                                         {
294                                                 messages.push_back(sanitized);
295                                                 strncpy(host,this->connectors[i].GetServerName().c_str(),160);
296                                                 log(DEBUG,"main: Connection::RecvPacket() got '%s' from %s",sanitized,host);
297                                                 
298                                         }
299                                         l = strtok(NULL,"\n");
300                                 }
301                                 return true;
302                         }
303                 }
304         }
305         // nothing new yet -- message and host will be undefined
306         return false;
307 }
308
309 long connection::GenKey()
310 {
311         srand(time(NULL));
312         return (random()*time(NULL));
313 }
314