]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
Tidy up some of the internals a bit, making things a bit more extensible and future...
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core */
15
16 #include "inspircd.h"
17 #include "socket.h"
18 #include "socketengine.h"
19
20 /** This will bind a socket to a port. It works for UDP/TCP.
21  * It can only bind to IP addresses, if you wish to bind to hostnames
22  * you should first resolve them using class 'Resolver'.
23  */ 
24 bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten)
25 {
26         /* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */
27         sockaddr* servaddr = new sockaddr[2];
28         memset(servaddr,0,sizeof(sockaddr)*2);
29
30         int ret, size;
31
32         if (*addr == '*')
33                 addr = "";
34
35 #ifdef IPV6
36         if (*addr)
37         {
38                 /* There is an address here. Is it ipv6? */
39                 if (strchr(addr,':'))
40                 {
41                         /* Yes it is */
42                         in6_addr addy;
43                         if (inet_pton(AF_INET6, addr, &addy) < 1)
44                         {
45                                 delete[] servaddr;
46                                 return false;
47                         }
48
49                         ((sockaddr_in6*)servaddr)->sin6_family = AF_INET6;
50                         memcpy(&(((sockaddr_in6*)servaddr)->sin6_addr), &addy, sizeof(in6_addr));
51                         ((sockaddr_in6*)servaddr)->sin6_port = htons(port);
52                         size = sizeof(sockaddr_in6);
53                 }
54                 else
55                 {
56                         /* No, its not */
57                         in_addr addy;
58                         if (inet_pton(AF_INET, addr, &addy) < 1)
59                         {
60                                 delete[] servaddr;
61                                 return false;
62                         }
63
64                         ((sockaddr_in*)servaddr)->sin_family = AF_INET;
65                         ((sockaddr_in*)servaddr)->sin_addr = addy;
66                         ((sockaddr_in*)servaddr)->sin_port = htons(port);
67                         size = sizeof(sockaddr_in);
68                 }
69         }
70         else
71         {
72                 if (port == -1)
73                 {
74                         /* Port -1: Means UDP IPV4 port binding - Special case
75                          * used by DNS engine.
76                          */
77                         ((sockaddr_in*)servaddr)->sin_family = AF_INET;
78                         ((sockaddr_in*)servaddr)->sin_addr.s_addr = htonl(INADDR_ANY);
79                         ((sockaddr_in*)servaddr)->sin_port = 0;
80                         size = sizeof(sockaddr_in);
81                 }
82                 else
83                 {
84                         /* Theres no address here, default to ipv6 bind to all */
85                         ((sockaddr_in6*)servaddr)->sin6_family = AF_INET6;
86                         memset(&(((sockaddr_in6*)servaddr)->sin6_addr), 0, sizeof(in6_addr));
87                         ((sockaddr_in6*)servaddr)->sin6_port = htons(port);
88                         size = sizeof(sockaddr_in6);
89                 }
90         }
91 #else
92         /* If we aren't built with ipv6, the choice becomes simple */
93         ((sockaddr_in*)servaddr)->sin_family = AF_INET;
94         if (*addr)
95         {
96                 /* There is an address here. */
97                 in_addr addy;
98                 if (inet_pton(AF_INET, addr, &addy) < 1)
99                 {
100                         delete[] servaddr;
101                         return false;
102                 }
103                 ((sockaddr_in*)servaddr)->sin_addr = addy;
104         }
105         else
106         {
107                 /* Bind ipv4 to all */
108                 ((sockaddr_in*)servaddr)->sin_addr.s_addr = htonl(INADDR_ANY);
109         }
110         /* Bind ipv4 port number */
111         ((sockaddr_in*)servaddr)->sin_port = htons(port);
112         size = sizeof(sockaddr_in);
113 #endif
114         ret = SE->Bind(sockfd, servaddr, size);
115         delete[] servaddr;
116
117         if (ret < 0)
118         {
119                 return false;
120         }
121         else
122         {
123                 if (dolisten)
124                 {
125                         if (SE->Listen(sockfd, Config->MaxConn) == -1)
126                         {
127                                 this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno));
128                                 return false;
129                         }
130                         else
131                         {
132                                 this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
133                                 SE->NonBlocking(sockfd);
134                                 return true;
135                         }
136                 }
137                 else
138                 {
139                         this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
140                         return true;
141                 }
142         }
143 }
144
145 // Open a TCP Socket
146 int irc::sockets::OpenTCPSocket(char* addr, int socktype)
147 {
148         int sockfd;
149         int on = 1;
150         addr = addr;
151         struct linger linger = { 0, 0 };
152 #ifdef IPV6
153         if (strchr(addr,':') || (!*addr))
154                 sockfd = socket (PF_INET6, socktype, 0);
155         else
156                 sockfd = socket (PF_INET, socktype, 0);
157         if (sockfd < 0)
158 #else
159         if ((sockfd = socket (PF_INET, socktype, 0)) < 0)
160 #endif
161         {
162                 return ERROR;
163         }
164         else
165         {
166                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
167                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
168                 linger.l_onoff = 1;
169                 linger.l_linger = 1;
170                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
171                 return (sockfd);
172         }
173 }
174
175 int InspIRCd::BindPorts(bool, int &ports_found, FailedPortList &failed_ports)
176 {
177         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
178         int bound = 0;
179         bool started_with_nothing = (Config->ports.size() == 0);
180         std::vector<std::pair<std::string, int> > old_ports;
181
182         /* XXX: Make a copy of the old ip/port pairs here */
183         for (std::vector<ListenSocket*>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o)
184                 old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort()));
185
186         for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
187         {
188                 Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
189                 Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
190                 Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
191                 
192                 if (strncmp(Addr, "::ffff:", 7) == 0)
193                         this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
194                 
195                 if ((!*Type) || (!strcmp(Type,"clients")))
196                 {
197                         irc::portparser portrange(configToken, false);
198                         int portno = -1;
199                         while (0 != (portno = portrange.GetToken()))
200                         {
201                                 if (*Addr == '*')
202                                         *Addr = 0;
203
204                                 bool skip = false;
205                                 for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
206                                 {
207                                         if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno))
208                                         {
209                                                 skip = true;
210                                                 /* XXX: Here, erase from our copy of the list */
211                                                 for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k)
212                                                 {
213                                                         if ((k->first == Addr) && (k->second == portno))
214                                                         {
215                                                                 old_ports.erase(k);
216                                                                 break;
217                                                         }
218                                                 }
219                                         }
220                                 }
221                                 if (!skip)
222                                 {
223                                         ListenSocket* ll = new ListenSocket(this, portno, Addr);
224                                         if (ll->GetFd() > -1)
225                                         {
226                                                 bound++;
227                                                 Config->ports.push_back(ll);
228                                         }
229                                         else
230                                         {
231                                                 failed_ports.push_back(std::make_pair((*Addr ? Addr : "*") + std::string(":") + ConvToStr(portno), strerror(errno)));
232                                         }
233                                         ports_found++;
234                                 }
235                         }
236                 }
237         }
238
239         /* XXX: Here, anything left in our copy list, close as removed */
240         if (!started_with_nothing)
241         {
242                 for (size_t k = 0; k < old_ports.size(); ++k)
243                 {
244                         for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
245                         {
246                                 if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second))
247                                 {
248                                         this->Logs->Log("SOCKET",DEFAULT,"Port binding %s:%d was removed from the config file, closing.", old_ports[k].first.c_str(), old_ports[k].second);
249                                         delete *n;
250                                         Config->ports.erase(n);
251                                         break;
252                                 }
253                         }
254                 }
255         }
256
257         return bound;
258 }
259
260 const char* irc::sockets::insp_ntoa(insp_inaddr n)
261 {
262         static char buf[1024];
263         inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
264         return buf;
265 }
266
267 int irc::sockets::insp_aton(const char* a, insp_inaddr* n)
268 {
269         return inet_pton(AF_FAMILY, a, n);
270 }
271
272