]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
Update copyrights for 2009.
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 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(const 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 (!*addr)
154         {
155                 sockfd = socket (PF_INET6, socktype, 0);
156                 if (sockfd < 0)
157                         sockfd = socket (PF_INET, socktype, 0);
158         }
159         else if (strchr(addr,':'))
160                 sockfd = socket (PF_INET6, socktype, 0);
161         else
162                 sockfd = socket (PF_INET, socktype, 0);
163         if (sockfd < 0)
164 #else
165         if ((sockfd = socket (PF_INET, socktype, 0)) < 0)
166 #endif
167         {
168                 return ERROR;
169         }
170         else
171         {
172                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
173                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
174                 linger.l_onoff = 1;
175                 linger.l_linger = 1;
176                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
177                 return (sockfd);
178         }
179 }
180
181 // XXX: it would be VERY nice to genericize this so all listen stuff (server/client) could use the one function. -- w00t
182 int InspIRCd::BindPorts(bool, int &ports_found, FailedPortList &failed_ports)
183 {
184         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
185         int bound = 0;
186         bool started_with_nothing = (Config->ports.size() == 0);
187         std::vector<std::pair<std::string, int> > old_ports;
188
189         /* XXX: Make a copy of the old ip/port pairs here */
190         for (std::vector<ListenSocketBase *>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o)
191                 old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort()));
192
193         for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
194         {
195                 Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
196                 Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
197                 Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
198                 
199                 if (strncmp(Addr, "::ffff:", 7) == 0)
200                         this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
201                 
202                 if ((!*Type) || (!strcmp(Type,"clients")))
203                 {
204                         irc::portparser portrange(configToken, false);
205                         int portno = -1;
206                         while (0 != (portno = portrange.GetToken()))
207                         {
208                                 if (*Addr == '*')
209                                         *Addr = 0;
210
211                                 bool skip = false;
212                                 for (std::vector<ListenSocketBase *>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
213                                 {
214                                         if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno))
215                                         {
216                                                 skip = true;
217                                                 /* XXX: Here, erase from our copy of the list */
218                                                 for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k)
219                                                 {
220                                                         if ((k->first == Addr) && (k->second == portno))
221                                                         {
222                                                                 old_ports.erase(k);
223                                                                 break;
224                                                         }
225                                                 }
226                                         }
227                                 }
228                                 if (!skip)
229                                 {
230                                         ClientListenSocket *ll = new ClientListenSocket(this, portno, Addr);
231                                         if (ll->GetFd() > -1)
232                                         {
233                                                 bound++;
234                                                 Config->ports.push_back(ll);
235                                         }
236                                         else
237                                         {
238                                                 failed_ports.push_back(std::make_pair((*Addr ? Addr : "*") + std::string(":") + ConvToStr(portno), strerror(errno)));
239                                         }
240                                         ports_found++;
241                                 }
242                         }
243                 }
244         }
245
246         /* XXX: Here, anything left in our copy list, close as removed */
247         if (!started_with_nothing)
248         {
249                 for (size_t k = 0; k < old_ports.size(); ++k)
250                 {
251                         for (std::vector<ListenSocketBase *>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
252                         {
253                                 if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second))
254                                 {
255                                         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);
256                                         delete *n;
257                                         Config->ports.erase(n);
258                                         break;
259                                 }
260                         }
261                 }
262         }
263
264         return bound;
265 }
266
267 const char* irc::sockets::insp_ntoa(insp_inaddr n)
268 {
269         static char buf[1024];
270         inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
271         return buf;
272 }
273
274 int irc::sockets::insp_aton(const char* a, insp_inaddr* n)
275 {
276         return inet_pton(AF_FAMILY, a, n);
277 }
278
279