]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/other/radius/radius.cpp
Merge branch 'stg-2.409' into stg-2.409-radius
[stg.git] / projects / stargazer / plugins / other / radius / radius.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
19  */
20
21 #include "radius.h"
22
23 #include "stg/store.h"
24 #include "stg/users.h"
25 #include "stg/plugin_creator.h"
26 #include "stg/common.h"
27
28 #include <algorithm>
29 #include <stdexcept>
30 #include <csignal>
31 #include <cerrno>
32 #include <cstring>
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/un.h> // UNIX
37 #include <netinet/in.h> // IP
38 #include <netinet/tcp.h> // TCP
39 #include <netdb.h>
40
41 using STG::Config;
42 using STG::Conn;
43
44 namespace
45 {
46
47 PLUGIN_CREATOR<RADIUS> creator;
48
49 }
50
51 extern "C" PLUGIN * GetPlugin()
52 {
53     return creator.GetPlugin();
54 }
55
56 RADIUS::RADIUS()
57     : m_config(),
58       m_running(false),
59       m_stopped(true),
60       m_users(NULL),
61       m_store(NULL),
62       m_listenSocket(0),
63       m_logger(GetPluginLogger(GetStgLogger(), "radius"))
64 {
65 }
66
67 int RADIUS::ParseSettings()
68 {
69     try {
70         m_config = STG::Config(m_settings);
71         return reconnect() ? 0 : -1;
72     } catch (const std::runtime_error& ex) {
73         m_logger("Failed to parse settings. %s", ex.what());
74         return -1;
75     }
76 }
77
78 int RADIUS::Start()
79 {
80     if (m_running)
81         return 0;
82
83     int res = pthread_create(&m_thread, NULL, run, this);
84     if (res == 0)
85         return 0;
86
87     m_error = strerror(res);
88     m_logger("Failed to create thread: '" + m_error + "'.");
89     return -1;
90 }
91
92 int RADIUS::Stop()
93 {
94     if (m_stopped)
95         return 0;
96
97     m_running = false;
98
99     for (size_t i = 0; i < 25 && !m_stopped; i++) {
100         struct timespec ts = {0, 200000000};
101         nanosleep(&ts, NULL);
102     }
103
104     if (m_stopped) {
105         pthread_join(m_thread, NULL);
106         return 0;
107     }
108
109     if (m_config.connectionType == Config::UNIX)
110         unlink(m_config.bindAddress.c_str());
111
112     m_error = "Failed to stop thread.";
113     m_logger(m_error);
114     return -1;
115 }
116 //-----------------------------------------------------------------------------
117 void* RADIUS::run(void* d)
118 {
119     sigset_t signalSet;
120     sigfillset(&signalSet);
121     pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
122
123     static_cast<RADIUS *>(d)->runImpl();
124
125     return NULL;
126 }
127
128 bool RADIUS::reconnect()
129 {
130     if (!m_conns.empty())
131     {
132         std::deque<STG::Conn *>::const_iterator it;
133         for (it = m_conns.begin(); it != m_conns.end(); ++it)
134             delete(*it);
135         m_conns.clear();
136     }
137     if (m_listenSocket != 0)
138     {
139         shutdown(m_listenSocket, SHUT_RDWR);
140         close(m_listenSocket);
141     }
142     if (m_config.connectionType == Config::UNIX)
143         m_listenSocket = createUNIX();
144     else
145         m_listenSocket = createTCP();
146     if (m_listenSocket == 0)
147         return false;
148     if (listen(m_listenSocket, 100) == -1)
149     {
150         m_error = std::string("Error starting to listen socket: ") + strerror(errno);
151         m_logger(m_error);
152         return false;
153     }
154     return true;
155 }
156
157 int RADIUS::createUNIX() const
158 {
159     int fd = socket(AF_UNIX, SOCK_STREAM, 0);
160     if (fd == -1)
161     {
162         m_error = std::string("Error creating UNIX socket: ") + strerror(errno);
163         m_logger(m_error);
164         return 0;
165     }
166     struct sockaddr_un addr;
167     memset(&addr, 0, sizeof(addr));
168     addr.sun_family = AF_UNIX;
169     strncpy(addr.sun_path, m_config.bindAddress.c_str(), m_config.bindAddress.length());
170     unlink(m_config.bindAddress.c_str());
171     if (bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1)
172     {
173         shutdown(fd, SHUT_RDWR);
174         close(fd);
175         m_error = std::string("Error binding UNIX socket: ") + strerror(errno);
176         m_logger(m_error);
177         return 0;
178     }
179     chown(m_config.bindAddress.c_str(), m_config.sockUID, m_config.sockGID);
180     if (m_config.sockMode != static_cast<mode_t>(-1))
181         chmod(m_config.bindAddress.c_str(), m_config.sockMode);
182     return fd;
183 }
184
185 int RADIUS::createTCP() const
186 {
187     addrinfo hints;
188     memset(&hints, 0, sizeof(addrinfo));
189
190     hints.ai_family = AF_INET;       /* Allow IPv4 */
191     hints.ai_socktype = SOCK_STREAM; /* Stream socket */
192     hints.ai_flags = AI_PASSIVE;     /* For wildcard IP address */
193     hints.ai_protocol = 0;           /* Any protocol */
194     hints.ai_canonname = NULL;
195     hints.ai_addr = NULL;
196     hints.ai_next = NULL;
197
198     addrinfo* ais = NULL;
199     int res = getaddrinfo(m_config.bindAddress.c_str(), m_config.portStr.c_str(), &hints, &ais);
200     if (res != 0)
201     {
202         m_error = "Error resolving address '" + m_config.bindAddress + "': " + gai_strerror(res);
203         m_logger(m_error);
204         return 0;
205     }
206
207     for (addrinfo* ai = ais; ai != NULL; ai = ai->ai_next)
208     {
209         int fd = socket(AF_INET, SOCK_STREAM, 0);
210         if (fd == -1)
211         {
212             m_error = std::string("Error creating TCP socket: ") + strerror(errno);
213             m_logger(m_error);
214             freeaddrinfo(ais);
215             return 0;
216         }
217         if (bind(fd, ai->ai_addr, ai->ai_addrlen) == -1)
218         {
219             shutdown(fd, SHUT_RDWR);
220             close(fd);
221             m_error = std::string("Error binding TCP socket: ") + strerror(errno);
222             m_logger(m_error);
223             continue;
224         }
225         freeaddrinfo(ais);
226         return fd;
227     }
228
229     m_error = "Failed to resolve '" + m_config.bindAddress;
230     m_logger(m_error);
231
232     freeaddrinfo(ais);
233     return 0;
234 }
235
236 void RADIUS::runImpl()
237 {
238     m_running = true;
239     m_stopped = false;
240
241     while (m_running) {
242         fd_set fds;
243
244         buildFDSet(fds);
245
246         struct timeval tv;
247         tv.tv_sec = 0;
248         tv.tv_usec = 500000;
249
250         int res = select(maxFD() + 1, &fds, NULL, NULL, &tv);
251         if (res < 0)
252         {
253             if (errno == EINTR)
254                 continue;
255             m_error = std::string("'select' is failed: '") + strerror(errno) + "'.";
256             m_logger(m_error);
257             break;
258         }
259
260         if (!m_running)
261             break;
262
263         if (res > 0)
264             handleEvents(fds);
265         else
266         {
267             for (std::deque<Conn*>::iterator it = m_conns.begin(); it != m_conns.end(); ++it)
268                 (*it)->tick();
269         }
270
271         cleanupConns();
272     }
273
274     m_stopped = true;
275 }
276
277 int RADIUS::maxFD() const
278 {
279     int maxFD = m_listenSocket;
280     std::deque<STG::Conn *>::const_iterator it;
281     for (it = m_conns.begin(); it != m_conns.end(); ++it)
282         if (maxFD < (*it)->sock())
283             maxFD = (*it)->sock();
284     return maxFD;
285 }
286
287 void RADIUS::buildFDSet(fd_set & fds) const
288 {
289     FD_ZERO(&fds);
290     FD_SET(m_listenSocket, &fds);
291     std::deque<STG::Conn *>::const_iterator it;
292     for (it = m_conns.begin(); it != m_conns.end(); ++it)
293         FD_SET((*it)->sock(), &fds);
294 }
295
296 void RADIUS::cleanupConns()
297 {
298     std::deque<STG::Conn *>::iterator pos;
299     for (pos = m_conns.begin(); pos != m_conns.end(); ++pos)
300         if (!(*pos)->isOk()) {
301             delete *pos;
302             *pos = NULL;
303         }
304
305     pos = std::remove(m_conns.begin(), m_conns.end(), static_cast<STG::Conn *>(NULL));
306     m_conns.erase(pos, m_conns.end());
307 }
308
309 void RADIUS::handleEvents(const fd_set & fds)
310 {
311     if (FD_ISSET(m_listenSocket, &fds))
312         acceptConnection();
313     else
314     {
315         std::deque<STG::Conn *>::iterator it;
316         for (it = m_conns.begin(); it != m_conns.end(); ++it)
317             if (FD_ISSET((*it)->sock(), &fds))
318                 (*it)->read();
319             else
320                 (*it)->tick();
321     }
322 }
323
324 void RADIUS::acceptConnection()
325 {
326     if (m_config.connectionType == Config::UNIX)
327         acceptUNIX();
328     else
329         acceptTCP();
330 }
331
332 void RADIUS::acceptUNIX()
333 {
334     struct sockaddr_un addr;
335     memset(&addr, 0, sizeof(addr));
336     socklen_t size = sizeof(addr);
337     int res = accept(m_listenSocket, reinterpret_cast<sockaddr*>(&addr), &size);
338     if (res == -1)
339     {
340         m_error = std::string("Failed to accept UNIX connection: ") + strerror(errno);
341         m_logger(m_error);
342         return;
343     }
344     printfd(__FILE__, "New UNIX connection: '%s'\n", addr.sun_path);
345     m_conns.push_back(new Conn(*m_users, m_logger, m_config, res, addr.sun_path));
346 }
347
348 void RADIUS::acceptTCP()
349 {
350     struct sockaddr_in addr;
351     memset(&addr, 0, sizeof(addr));
352     socklen_t size = sizeof(addr);
353     int res = accept(m_listenSocket, reinterpret_cast<sockaddr*>(&addr), &size);
354     if (res == -1)
355     {
356         m_error = std::string("Failed to accept TCP connection: ") + strerror(errno);
357         m_logger(m_error);
358         return;
359     }
360     std::string remote = inet_ntostring(addr.sin_addr.s_addr) + ":" + x2str(ntohs(addr.sin_port));
361     printfd(__FILE__, "New TCP connection: '%s'\n", remote.c_str());
362     m_conns.push_back(new Conn(*m_users, m_logger, m_config, res, remote));
363 }