summaryrefslogtreecommitdiff
path: root/include/inspsocket.h
blob: 221b92cc6feb94923f47423e46fd8f02a1637a38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
 * InspIRCd -- Internet Relay Chat Daemon
 *
 *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
 *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
 *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
 *   Copyright (C) 2006-2007 Craig Edwards <craigedwards@brainbox.cc>
 *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
 *
 * This file is part of InspIRCd.  InspIRCd is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, version 2.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#pragma once

#include "timer.h"

class IOHook;

/**
 * States which a socket may be in
 */
enum BufferedSocketState
{
	/** Socket disconnected */
	I_DISCONNECTED,
	/** Socket connecting */
	I_CONNECTING,
	/** Socket fully connected */
	I_CONNECTED,
	/** Socket has an error */
	I_ERROR
};

/**
 * Error types which a socket may exhibit
 */
enum BufferedSocketError
{
	/** No error */
	I_ERR_NONE,
	/** Socket was closed by peer */
	I_ERR_DISCONNECT,
	/** Socket connect timed out */
	I_ERR_TIMEOUT,
	/** Socket could not be created */
	I_ERR_SOCKET,
	/** Socket could not connect (refused) */
	I_ERR_CONNECT,
	/** Socket could not bind to local port/ip */
	I_ERR_BIND,
	/** Socket could not write data */
	I_ERR_WRITE,
	/** No more file descriptors left to create socket! */
	I_ERR_NOMOREFDS,
	/** Some other error */
	I_ERR_OTHER
};

/* Required forward declarations */
class BufferedSocket;

/** Used to time out socket connections
 */
class CoreExport SocketTimeout : public Timer
{
 private:
	/** BufferedSocket the class is attached to
	 */
	BufferedSocket* sock;

	/** File descriptor of class this is attached to
	 */
	int sfd;

 public:
	/** Create a socket timeout class
	 * @param fd File descriptor of BufferedSocket
	 * @param thesock BufferedSocket to attach to
	 * @param secs_from_now Seconds from now to time out
	 * @param now The current time
	 */
	SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now) : Timer(secs_from_now), sock(thesock), sfd(fd) { }

	/** Handle tick event
	 */
	virtual bool Tick(time_t now);
};

/**
 * StreamSocket is a class that wraps a TCP socket and handles send
 * and receive queues, including passing them to IO hooks
 */
class CoreExport StreamSocket : public EventHandler
{
	/** The IOHook that handles raw I/O for this socket, or NULL */
	IOHook* iohook;

	/** Private send queue. Note that individual strings may be shared
	 */
	std::deque<std::string> sendq;
	/** Length, in bytes, of the sendq */
	size_t sendq_len;
	/** Error - if nonempty, the socket is dead, and this is the reason. */
	std::string error;

	/** Check if the socket has an error set, if yes, call OnError
	 * @param err Error to pass to OnError()
	 */
	void CheckError(BufferedSocketError err);

	/** Read data from the socket into the recvq, if successful call OnDataReady()
	 */
	void DoRead();

 protected:
	std::string recvq;
 public:
	StreamSocket() : iohook(NULL), sendq_len(0) {}
	IOHook* GetIOHook() const;
	void AddIOHook(IOHook* hook);
	void DelIOHook();

	/** Flush the send queue
	 */
	void DoWrite();

	/** Called by the socket engine on a read event
	 */
	void OnEventHandlerRead() CXX11_OVERRIDE;

	/** Called by the socket engine on a write event
	 */
	void OnEventHandlerWrite() CXX11_OVERRIDE;

	/** Called by the socket engine on error
	 * @param errcode Error
	 */
	void OnEventHandlerError(int errcode) CXX11_OVERRIDE;

	/** Sets the error message for this socket. Once set, the socket is dead. */
	void SetError(const std::string& err) { if (error.empty()) error = err; }

	/** Gets the error message for this socket. */
	const std::string& getError() const { return error; }

	/** Called when new data is present in recvq */
	virtual void OnDataReady() = 0;
	/** Called when the socket gets an error from socket engine or IO hook */
	virtual void OnError(BufferedSocketError e) = 0;

	/** Send the given data out the socket, either now or when writes unblock
	 */
	void WriteData(const std::string& data);
	/** Convenience function: read a line from the socket
	 * @param line The line read
	 * @param delim The line delimiter
	 * @return true if a line was read
	 */
	bool GetNextLine(std::string& line, char delim = '\n');
	/** Useful for implementing sendq exceeded */
	inline size_t getSendQSize() const { return sendq_len; }

	/**
	 * Close the socket, remove from socket engine, etc
	 */
	virtual void Close();
	/** This ensures that close is called prior to destructor */
	virtual CullResult cull();
};
/**
 * BufferedSocket is an extendable socket class which modules
 * can use for TCP socket support. It is fully integrated
 * into InspIRCds socket loop and attaches its sockets to
 * the core's instance of the SocketEngine class, meaning
 * that all use is fully asynchronous.
 *
 * To use BufferedSocket, you must inherit a class from it.
 */
class CoreExport BufferedSocket : public StreamSocket
{
 public:
	/** Timeout object or NULL
	 */
	SocketTimeout* Timeout;

	/**
	 * The state for this socket, either
	 * listening, connecting, connected
	 * or error.
	 */
	BufferedSocketState state;

	BufferedSocket();
	/**
	 * This constructor is used to associate
	 * an existing connecting with an BufferedSocket
	 * class. The given file descriptor must be
	 * valid, and when initialized, the BufferedSocket
	 * will be placed in CONNECTED state.
	 */
	BufferedSocket(int newfd);

	/** Begin connection to the given address
	 * This will create a socket, register with socket engine, and start the asynchronous
	 * connection process. If an error is detected at this point (such as out of file descriptors),
	 * OnError will be called; otherwise, the state will become CONNECTING.
	 * @param ipaddr Address to connect to
	 * @param aport Port to connect on
	 * @param maxtime Time to wait for connection
	 * @param connectbindip Address to bind to (if NULL, no bind will be done)
	 */
	void DoConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip);

	/** This method is called when an outbound connection on your socket is
	 * completed.
	 */
	virtual void OnConnected();

	/** When there is data waiting to be read on a socket, the OnDataReady()
	 * method is called.
	 */
	virtual void OnDataReady() = 0;

	/**
	 * When an outbound connection fails, and the attempt times out, you
	 * will receive this event.  The method will trigger once maxtime
	 * seconds are reached (as given in the constructor) just before the
	 * socket's descriptor is closed.  A failed DNS lookup may cause this
	 * event if the DNS server is not responding, as well as a failed
	 * connect() call, because DNS lookups are nonblocking as implemented by
	 * this class.
	 */
	virtual void OnTimeout();

	virtual ~BufferedSocket();
 protected:
	void OnEventHandlerWrite() CXX11_OVERRIDE;
	BufferedSocketError BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned long timeout);
	BufferedSocketError BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip);
};

inline IOHook* StreamSocket::GetIOHook() const { return iohook; }
inline void StreamSocket::AddIOHook(IOHook* hook) { iohook = hook; }
inline void StreamSocket::DelIOHook() { iohook = NULL; }