]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspsocket.cpp
Checks for negative fd's when adding them to the socketengine so we can generate...
[user/henk/code/inspircd.git] / src / inspsocket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 #include <string>
18 #include <sstream>
19 #include <iostream>
20 #include <fstream>
21 #include <stdexcept>
22 #include "inspircd_config.h"
23 #include "socket.h"
24 #include "inspircd.h"
25 #include "configreader.h"
26 #include "inspstring.h"
27 #include "helperfuncs.h"
28 #include "socketengine.h"
29
30
31 extern InspIRCd* ServerInstance;
32 extern ServerConfig* Config;
33 extern time_t TIME;
34
35 InspSocket* socket_ref[MAX_DESCRIPTORS];
36
37 InspSocket::InspSocket()
38 {
39         this->state = I_DISCONNECTED;
40         this->fd = -1;
41         this->ClosePending = false;
42 }
43
44 InspSocket::InspSocket(int newfd, char* ip)
45 {
46         this->fd = newfd;
47         this->state = I_CONNECTED;
48         strlcpy(this->IP,ip,MAXBUF);
49         this->ClosePending = false;
50         if (this->fd > -1)
51         {
52                 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
53                 socket_ref[this->fd] = this;
54         }
55 }
56
57 InspSocket::InspSocket(const std::string &ahost, int aport, bool listening, unsigned long maxtime) : fd(-1)
58 {
59         strlcpy(host,ahost.c_str(),MAXBUF);
60         this->ClosePending = false;
61         if (listening) {
62                 if ((this->fd = OpenTCPSocket()) == ERROR)
63                 {
64                         this->fd = -1;
65                         this->state = I_ERROR;
66                         this->OnError(I_ERR_SOCKET);
67                         this->ClosePending = true;
68                         log(DEBUG,"OpenTCPSocket() error");
69                         return;
70                 }
71                 else
72                 {
73                         if (!BindSocket(this->fd,this->client,this->server,aport,(char*)ahost.c_str()))
74                         {
75                                 this->Close();
76                                 this->fd = -1;
77                                 this->state = I_ERROR;
78                                 this->OnError(I_ERR_BIND);
79                                 this->ClosePending = true;
80                                 log(DEBUG,"BindSocket() error %s",strerror(errno));
81                                 return;
82                         }
83                         else
84                         {
85                                 this->state = I_LISTENING;
86                                 if (this->fd > -1)
87                                 {
88                                         ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
89                                         socket_ref[this->fd] = this;
90                                 }
91                                 log(DEBUG,"New socket now in I_LISTENING state");
92                                 return;
93                         }
94                 }                       
95         }
96         else
97         {
98                 strlcpy(this->host,ahost.c_str(),MAXBUF);
99                 this->port = aport;
100
101                 if (!inet_aton(host,&addy))
102                 {
103                         log(DEBUG,"Attempting to resolve %s",this->host);
104                         /* Its not an ip, spawn the resolver */
105                         this->dns.SetNS(std::string(Config->DNSServer));
106                         this->dns.ForwardLookupWithFD(host,fd);
107                         timeout_end = time(NULL) + maxtime;
108                         timeout = false;
109                         this->state = I_RESOLVING;
110                         socket_ref[this->fd] = this;
111                 }
112                 else
113                 {
114                         log(DEBUG,"No need to resolve %s",this->host);
115                         strlcpy(this->IP,host,MAXBUF);
116                         timeout_end = time(NULL) + maxtime;
117                         this->DoConnect();
118                 }
119         }
120 }
121
122 void InspSocket::SetQueues(int nfd)
123 {
124         // attempt to increase socket sendq and recvq as high as its possible
125         int sendbuf = 32768;
126         int recvbuf = 32768;
127         setsockopt(nfd,SOL_SOCKET,SO_SNDBUF,(const void *)&sendbuf,sizeof(sendbuf));
128         setsockopt(nfd,SOL_SOCKET,SO_RCVBUF,(const void *)&recvbuf,sizeof(sendbuf));
129 }
130
131 bool InspSocket::DoResolve()
132 {
133         log(DEBUG,"In DoResolve(), trying to resolve IP");
134         if (this->dns.HasResult())
135         {
136                 log(DEBUG,"Socket has result");
137                 std::string res_ip = dns.GetResultIP();
138                 if (res_ip != "")
139                 {
140                         log(DEBUG,"Socket result set to %s",res_ip.c_str());
141                         strlcpy(this->IP,res_ip.c_str(),MAXBUF);
142                         socket_ref[this->fd] = NULL;
143                 }
144                 else
145                 {
146                         log(DEBUG,"Socket DNS failure");
147                         this->Close();
148                         this->state = I_ERROR;
149                         this->OnError(I_ERR_RESOLVE);
150                         this->fd = -1;
151                         this->ClosePending = true;
152                         return false;
153                 }
154                 return this->DoConnect();
155         }
156         log(DEBUG,"No result for socket yet!");
157         return true;
158 }
159
160 bool InspSocket::DoConnect()
161 {
162         log(DEBUG,"In DoConnect()");
163         if ((this->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
164         {
165                 log(DEBUG,"Cant socket()");
166                 this->state = I_ERROR;
167                 this->OnError(I_ERR_SOCKET);
168                 this->fd = -1;
169                 return false;
170         }
171
172         log(DEBUG,"Part 2 DoConnect() %s",this->IP);
173         inet_aton(this->IP,&addy);
174         addr.sin_family = AF_INET;
175         addr.sin_addr = addy;
176         addr.sin_port = htons(this->port);
177
178         int flags;
179         flags = fcntl(this->fd, F_GETFL, 0);
180         fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
181
182         if (connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)) == -1)
183         {
184                 if (errno != EINPROGRESS)
185                 {
186                         log(DEBUG,"Error connect() %d: %s",this->fd,strerror(errno));
187                         this->OnError(I_ERR_CONNECT);
188                         this->Close();
189                         this->state = I_ERROR;
190                         this->fd = -1;
191                         this->ClosePending = true;
192                         return false;
193                 }
194         }
195         this->state = I_CONNECTING;
196         if (this->fd > -1)
197         {
198                 ServerInstance->SE->AddFd(this->fd,false,X_ESTAB_MODULE);
199                 socket_ref[this->fd] = this;
200                 this->SetQueues(this->fd);
201         }
202         log(DEBUG,"Returning true from InspSocket::DoConnect");
203         return true;
204 }
205
206
207 void InspSocket::Close()
208 {
209         if (this->fd != -1)
210         {
211                 this->OnClose();
212                 shutdown(this->fd,2);
213                 close(this->fd);
214                 socket_ref[this->fd] = NULL;
215                 this->ClosePending = true;
216                 this->fd = -1;
217         }
218 }
219
220 std::string InspSocket::GetIP()
221 {
222         return this->IP;
223 }
224
225 char* InspSocket::Read()
226 {
227         if ((fd < 0) || (fd > MAX_DESCRIPTORS))
228                 return NULL;
229         int n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0);
230         if ((n > 0) && (n <= (int)sizeof(this->ibuf)))
231         {
232                 ibuf[n] = 0;
233                 return ibuf;
234         }
235         else
236         {
237                 if (errno == EAGAIN)
238                 {
239                         return "";
240                 }
241                 else
242                 {
243                         log(DEBUG,"EOF or error on socket: %s",strerror(errno));
244                         return NULL;
245                 }
246         }
247 }
248
249 void InspSocket::MarkAsClosed()
250 {
251         log(DEBUG,"Marked as closed");
252         this->ClosePending = true;
253 }
254
255 // There are two possible outcomes to this function.
256 // It will either write all of the data, or an undefined amount.
257 // If an undefined amount is written the connection has failed
258 // and should be aborted.
259 int InspSocket::Write(const std::string &data)
260 {
261         if (this->ClosePending)
262                 return false;
263
264         /*int result = write(this->fd,data.c_str(),data.length());
265         if (result < 1)
266                 return false;
267         return true;*/
268
269         /* Try and append the data to the back of the queue, and send it on its way
270          */
271         outbuffer.push_back(data);
272         return (!this->FlushWriteBuffer());
273 }
274
275 bool InspSocket::FlushWriteBuffer()
276 {
277         if (this->ClosePending)
278                 return true;
279
280         if ((this->fd > -1) && (this->state == I_CONNECTED))
281         {
282                 if (outbuffer.size())
283                 {
284                         int result = write(this->fd,outbuffer[0].c_str(),outbuffer[0].length());
285                         if (result > 0)
286                         {
287                                 if ((unsigned int)result == outbuffer[0].length())
288                                 {
289                                         /* The whole block was written (usually a line)
290                                          * Pop the block off the front of the queue
291                                          */
292                                         outbuffer.pop_front();
293                                 }
294                                 else
295                                 {
296                                         std::string temp = outbuffer[0].substr(result);
297                                         outbuffer[0] = temp;
298                                 }
299                         }
300                         else if ((result == -1) && (errno != EAGAIN))
301                         {
302                                 log(DEBUG,"Write error on socket: %s",strerror(errno));
303                                 this->OnError(I_ERR_WRITE);
304                                 this->state = I_ERROR;
305                                 this->ClosePending = true;
306                                 return true;
307                         }
308                 }
309         }
310         return (fd < 0);
311 }
312
313 bool InspSocket::Timeout(time_t current)
314 {
315         if (!socket_ref[this->fd] || !ServerInstance->SE->HasFd(this->fd))
316         {
317                 log(DEBUG,"No FD or socket ref");
318                 return false;
319         }
320
321         if (this->ClosePending)
322         {
323                 log(DEBUG,"Close is pending");
324                 return true;
325         }
326
327         if (((this->state == I_RESOLVING) || (this->state == I_CONNECTING)) && (current > timeout_end))
328         {
329                 log(DEBUG,"Timed out, current=%lu timeout_end=%lu");
330                 // for non-listening sockets, the timeout can occur
331                 // which causes termination of the connection after
332                 // the given number of seconds without a successful
333                 // connection.
334                 this->OnTimeout();
335                 this->OnError(I_ERR_TIMEOUT);
336                 timeout = true;
337                 this->state = I_ERROR;
338                 this->ClosePending = true;
339                 return true;
340         }
341         return this->FlushWriteBuffer();
342 }
343
344 bool InspSocket::Poll()
345 {
346         if (!socket_ref[this->fd] || !ServerInstance->SE->HasFd(this->fd))
347                 return false;
348
349         int incoming = -1;
350         bool n = true;
351
352         if ((fd < 0) || (fd > MAX_DESCRIPTORS) || (this->ClosePending))
353                 return false;
354
355         switch (this->state)
356         {
357                 case I_RESOLVING:
358                         log(DEBUG,"State = I_RESOLVING, calling DoResolve()");
359                         return this->DoResolve();
360                 break;
361                 case I_CONNECTING:
362                         log(DEBUG,"State = I_CONNECTING");
363                         this->SetState(I_CONNECTED);
364                         /* Our socket was in write-state, so delete it and re-add it
365                          * in read-state.
366                          */
367                         if (this->fd > -1)
368                         {
369                                 ServerInstance->SE->DelFd(this->fd);
370                                 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
371                         }
372                         return this->OnConnected();
373                 break;
374                 case I_LISTENING:
375                         length = sizeof (client);
376                         incoming = accept (this->fd, (sockaddr*)&client,&length);
377                         this->SetQueues(incoming);
378                         this->OnIncomingConnection(incoming,inet_ntoa(client.sin_addr));
379                         return true;
380                 break;
381                 case I_CONNECTED:
382                         n = this->OnDataReady();
383                         /* Flush any pending, but not till after theyre done with the event
384                          * so there are less write calls involved.
385                          * Both FlushWriteBuffer AND the return result of OnDataReady must
386                          * return true for this to be ok.
387                          */
388                         if (this->FlushWriteBuffer())
389                                 return false;
390                         return n;
391                 break;
392                 default:
393                 break;
394         }
395         return true;
396 }
397
398 void InspSocket::SetState(InspSocketState s)
399 {
400         log(DEBUG,"Socket state change");
401         this->state = s;
402 }
403
404 InspSocketState InspSocket::GetState()
405 {
406         return this->state;
407 }
408
409 int InspSocket::GetFd()
410 {
411         return this->fd;
412 }
413
414 bool InspSocket::OnConnected() { return true; }
415 void InspSocket::OnError(InspSocketError e) { return; }
416 int InspSocket::OnDisconnect() { return 0; }
417 int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; }
418 bool InspSocket::OnDataReady() { return true; }
419 void InspSocket::OnTimeout() { return; }
420 void InspSocket::OnClose() { return; }
421
422 InspSocket::~InspSocket()
423 {
424         this->Close();
425 }