]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
More nonblocking dns stuffs
[user/henk/code/inspircd.git] / src / socket.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 using namespace std;
18
19 #include "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <string>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <poll.h>
29 #include <sstream>
30 #include <iostream>
31 #include <fstream>
32 #include "socket.h"
33 #include "inspircd.h"
34 #include "inspircd_io.h"
35 #include "inspstring.h"
36 #include "helperfuncs.h"
37 #include "socketengine.h"
38
39
40 extern InspIRCd* ServerInstance;
41 extern ServerConfig* Config;
42 extern time_t TIME;
43
44 InspSocket* socket_ref[MAX_DESCRIPTORS];
45
46 InspSocket::InspSocket()
47 {
48         this->state = I_DISCONNECTED;
49 }
50
51 InspSocket::InspSocket(int newfd, char* ip)
52 {
53         this->fd = newfd;
54         this->state = I_CONNECTED;
55         this->IP = ip;
56         ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
57         socket_ref[this->fd] = this;
58 }
59
60 InspSocket::InspSocket(std::string host, int port, bool listening, unsigned long maxtime)
61 {
62         if (listening) {
63                 if ((this->fd = OpenTCPSocket()) == ERROR)
64                 {
65                         this->fd = -1;
66                         this->state = I_ERROR;
67                         this->OnError(I_ERR_SOCKET);
68                         log(DEBUG,"OpenTCPSocket() error");
69                         return;
70                 }
71                 else
72                 {
73                         if (BindSocket(this->fd,this->client,this->server,port,(char*)host.c_str()) == ERROR)
74                         {
75                                 this->Close();
76                                 this->fd = -1;
77                                 this->state = I_ERROR;
78                                 this->OnError(I_ERR_BIND);
79                                 log(DEBUG,"BindSocket() error %s",strerror(errno));
80                                 return;
81                         }
82                         else
83                         {
84                                 this->state = I_LISTENING;
85                                 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
86                                 socket_ref[this->fd] = this;
87                                 log(DEBUG,"New socket now in I_LISTENING state");
88                                 return;
89                         }
90                 }                       
91         }
92         else
93         {
94                 this->host = host;
95                 this->port = port;
96
97                 if (!inet_aton(host.c_str(),&addy))
98                 {
99                         log(DEBUG,"Attempting to resolve %s",this->host.c_str());
100                         /* Its not an ip, spawn the resolver */
101                         this->dns.SetNS(std::string(Config->DNSServer));
102                         this->dns.ForwardLookupWithFD(host,fd);
103                         timeout_end = time(NULL)+maxtime;
104                         timeout = false;
105                         this->state = I_RESOLVING;
106                         socket_ref[this->fd] = this;
107                 }
108                 else
109                 {
110                         log(DEBUG,"No need to resolve %s",this->host.c_str());
111                         this->IP = host;
112                         this->DoConnect();
113                 }
114         }
115 }
116
117 bool InspSocket::DoResolve()
118 {
119         log(DEBUG,"In DoResolve(), trying to resolve IP");
120         if (this->dns.HasResult())
121         {
122                 log(DEBUG,"Socket has result");
123                 std::string res_ip = dns.GetResultIP();
124                 if (res_ip != "")
125                 {
126                         log(DEBUG,"Socket result set to %s",res_ip.c_str());
127                         this->IP = res_ip;
128                         socket_ref[this->fd] = NULL;
129                 }
130                 else
131                 {
132                         log(DEBUG,"Socket DNS failure");
133                         this->Close();
134                         this->state = I_ERROR;
135                         this->OnError(I_ERR_RESOLVE);
136                         return false;
137                 }
138                 return this->DoConnect();
139         }
140         log(DEBUG,"No result for socket yet!");
141         return true;
142 }
143
144 bool InspSocket::DoConnect()
145 {
146         log(DEBUG,"In DoConnect()");
147         if ((this->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
148         {
149                 log(DEBUG,"Cant socket()");
150                 this->state = I_ERROR;
151                 this->OnError(I_ERR_SOCKET);
152                 return false;
153         }
154
155         log(DEBUG,"Part 2 DoConnect() %s",this->IP.c_str());
156         inet_aton(this->IP.c_str(),&addy);
157         addr.sin_family = AF_INET;
158         addr.sin_addr = addy;
159         addr.sin_port = htons(this->port);
160
161         int flags;
162         flags = fcntl(this->fd, F_GETFL, 0);
163         fcntl(this->fd, F_SETFL, flags | O_NONBLOCK);
164
165         if(connect(this->fd, (sockaddr*)&this->addr,sizeof(this->addr)) == -1)
166         {
167                 if (errno != EINPROGRESS)
168                 {
169                         log(DEBUG,"Error connect() %d: %s",this->fd,strerror(errno));
170                         this->OnError(I_ERR_CONNECT);
171                         this->state = I_ERROR;
172                         this->Close();
173                         return false;
174                 }
175         }
176         this->state = I_CONNECTING;
177         ServerInstance->SE->AddFd(this->fd,false,X_ESTAB_MODULE);
178         socket_ref[this->fd] = this;
179         return true;
180 }
181
182
183 void InspSocket::Close()
184 {
185         if (this->fd != -1)
186         {
187                 this->OnClose();
188                 shutdown(this->fd,2);
189                 close(this->fd);
190                 socket_ref[this->fd] = NULL;
191                 this->fd = -1;
192         }
193 }
194
195 std::string InspSocket::GetIP()
196 {
197         return this->IP;
198 }
199
200 char* InspSocket::Read()
201 {
202         int n = recv(this->fd,this->ibuf,sizeof(this->ibuf),0);
203         if (n > 0)
204         {
205                 ibuf[n] = 0;
206                 return ibuf;
207         }
208         else
209         {
210                 if (n == EAGAIN)
211                 {
212                         return "";
213                 }
214                 else
215                 {
216                         log(DEBUG,"EOF or error on socket");
217                         return NULL;
218                 }
219         }
220 }
221
222 // There are two possible outcomes to this function.
223 // It will either write all of the data, or an undefined amount.
224 // If an undefined amount is written the connection has failed
225 // and should be aborted.
226 int InspSocket::Write(std::string data)
227 {
228         this->Buffer = this->Buffer + data;
229         this->FlushWriteBuffer();
230         return data.length();
231 }
232
233 void InspSocket::FlushWriteBuffer()
234 {
235         int result = 0;
236         if (this->Buffer.length())
237         {
238                 result = send(this->fd,this->Buffer.c_str(),this->Buffer.length(),0);
239                 if (result > 0)
240                 {
241                         /* If we wrote some, advance the buffer forwards */
242                         char* n = (char*)this->Buffer.c_str();
243                         n += result;
244                         this->Buffer = n;
245                 }
246         }
247 }
248
249 bool InspSocket::Timeout(time_t current)
250 {
251         if (((this->state == I_RESOLVING) || (this->state == I_CONNECTING)) && (current > timeout_end))
252         {
253                 // for non-listening sockets, the timeout can occur
254                 // which causes termination of the connection after
255                 // the given number of seconds without a successful
256                 // connection.
257                 this->OnTimeout();
258                 this->OnError(I_ERR_TIMEOUT);
259                 timeout = true;
260                 this->state = I_ERROR;
261                 return true;
262         }
263         if (this->Buffer.length())
264                 this->FlushWriteBuffer();
265         return false;
266 }
267
268 bool InspSocket::Poll()
269 {
270         int incoming = -1;
271         
272         switch (this->state)
273         {
274                 case I_RESOLVING:
275                         log(DEBUG,"State = I_RESOLVING, calling DoResolve()");
276                         return this->DoResolve();
277                 break;
278                 case I_CONNECTING:
279                         log(DEBUG,"State = I_CONNECTED");
280                         this->SetState(I_CONNECTED);
281                         /* Our socket was in write-state, so delete it and re-add it
282                          * in read-state.
283                          */
284                         ServerInstance->SE->DelFd(this->fd);
285                         ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_MODULE);
286                         return this->OnConnected();
287                 break;
288                 case I_LISTENING:
289                         length = sizeof (client);
290                         incoming = accept (this->fd, (sockaddr*)&client,&length);
291                         this->OnIncomingConnection(incoming,inet_ntoa(client.sin_addr));
292                         return true;
293                 break;
294                 case I_CONNECTED:
295                         return this->OnDataReady();
296                 break;
297                 default:
298                 break;
299         }
300
301         if (this->Buffer.length())
302                 this->FlushWriteBuffer();
303
304         return true;
305 }
306
307 void InspSocket::SetState(InspSocketState s)
308 {
309         log(DEBUG,"Socket state change");
310         this->state = s;
311 }
312
313 InspSocketState InspSocket::GetState()
314 {
315         return this->state;
316 }
317
318 int InspSocket::GetFd()
319 {
320         return this->fd;
321 }
322
323 bool InspSocket::OnConnected() { return true; }
324 void InspSocket::OnError(InspSocketError e) { return; }
325 int InspSocket::OnDisconnect() { return 0; }
326 int InspSocket::OnIncomingConnection(int newfd, char* ip) { return 0; }
327 bool InspSocket::OnDataReady() { return true; }
328 void InspSocket::OnTimeout() { return; }
329 void InspSocket::OnClose() { return; }
330
331 InspSocket::~InspSocket()
332 {
333         this->Close();
334 }
335