]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/capab.cpp
744e827bfb7ddfae3563441a57b7964231a64dfa
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / capab.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 #include "inspircd.h"
15 #include "commands/cmd_whois.h"
16 #include "commands/cmd_stats.h"
17 #include "socket.h"
18 #include "wildcard.h"
19 #include "xline.h"
20 #include "transport.h"
21 #include "m_hash.h"
22 #include "socketengine.h"
23
24 #include "m_spanningtree/main.h"
25 #include "m_spanningtree/utils.h"
26 #include "m_spanningtree/treeserver.h"
27 #include "m_spanningtree/link.h"
28 #include "m_spanningtree/treesocket.h"
29 #include "m_spanningtree/resolvers.h"
30 #include "m_spanningtree/handshaketimer.h"
31
32 /* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h m_hash.h */
33
34 std::string TreeSocket::MyCapabilities()
35 {
36         std::vector<std::string> modlist = this->Instance->Modules->GetAllModuleNames(VF_COMMON);
37         std::string capabilities;
38         sort(modlist.begin(),modlist.end());
39         for (unsigned int i = 0; i < modlist.size(); i++)
40         {
41                 if (i)
42                         capabilities = capabilities + ",";
43                 capabilities = capabilities + modlist[i];
44         }
45         return capabilities;
46 }
47
48 void TreeSocket::SendCapabilities()
49 {
50         if (sentcapab)
51                 return;
52
53         sentcapab = true;
54         irc::commasepstream modulelist(MyCapabilities());
55         this->WriteLine("CAPAB START");
56
57         /* Send module names, split at 509 length */
58         std::string item;
59         std::string line = "CAPAB MODULES ";
60         while (modulelist.GetToken(item))
61         {
62                 if (line.length() + item.length() + 1 > 509)
63                 {
64                         this->WriteLine(line);
65                         line = "CAPAB MODULES ";
66                 }
67
68                 if (line != "CAPAB MODULES ")
69                         line.append(",");
70
71                 line.append(item);
72         }
73         if (line != "CAPAB MODULES ")
74                 this->WriteLine(line);
75
76         int ip6 = 0;
77         int ip6support = 0;
78 #ifdef IPV6
79         ip6 = 1;
80 #endif
81 #ifdef SUPPORT_IP6LINKS
82         ip6support = 1;
83 #endif
84         std::string extra;
85         /* Do we have sha256 available? If so, we send a challenge */
86         if (Utils->ChallengeResponse && (Instance->Modules->Find("m_sha256.so")))
87         {
88                 this->SetOurChallenge(RandString(20));
89                 extra = " CHALLENGE=" + this->GetOurChallenge();
90         }
91
92         this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion)+extra+" PREFIX="+Instance->Modes->BuildPrefixes()+" CHANMODES="+Instance->Modes->ChanModes()+" SVSPART=1");
93
94         this->WriteLine("CAPAB END");
95 }
96
97 /* Check a comma seperated list for an item */
98 bool TreeSocket::HasItem(const std::string &list, const std::string &item)
99 {
100         irc::commasepstream seplist(list);
101         std::string item2;
102
103         while (seplist.GetToken(item2))
104         {
105                 if (item2 == item)
106                         return true;
107         }
108         return false;
109 }
110
111 /* Isolate and return the elements that are different between two comma seperated lists */
112 std::string TreeSocket::ListDifference(const std::string &one, const std::string &two)
113 {
114         irc::commasepstream list_one(one);
115         std::string item;
116         std::string result;
117         while (list_one.GetToken(item))
118         {
119                 if (!HasItem(two, item))
120                 {
121                         result.append(" ");
122                         result.append(item);
123                 }
124         }
125         return result;
126 }
127
128 bool TreeSocket::Capab(const std::deque<std::string> &params)
129 {
130         if (params.size() < 1)
131         {
132                 this->SendError("Invalid number of parameters for CAPAB - Mismatched version");
133                 return false;
134         }
135         if (params[0] == "START")
136         {
137                 this->ModuleList.clear();
138                 this->CapKeys.clear();
139         }
140         else if (params[0] == "END")
141         {
142                 std::string reason;
143                 int ip6support = 0;
144 #ifdef SUPPORT_IP6LINKS
145                 ip6support = 1;
146 #endif
147                 /* Compare ModuleList and check CapKeys...
148                  * Maybe this could be tidier? -- Brain
149                  */
150                 if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length()))
151                 {
152                         std::string diff = ListDifference(this->ModuleList, this->MyCapabilities());
153                         if (!diff.length())
154                         {
155                                 diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList);
156                         }
157                         else
158                         {
159                                 diff = "this server:" + diff;
160                         }
161                         if (diff.length() == 12)
162                                 reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists.";
163                         else
164                                 reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff;
165                 }
166
167                 cap_validation valid_capab[] = { 
168                         {"Maximum nickname lengths differ or remote nickname length not specified", "NICKMAX", NICKMAX},
169                         {"Maximum ident lengths differ or remote ident length not specified", "IDENTMAX", IDENTMAX},
170                         {"Maximum channel lengths differ or remote channel length not specified", "CHANMAX", CHANMAX},
171                         {"Maximum modes per line differ or remote modes per line not specified", "MAXMODES", MAXMODES},
172                         {"Maximum quit lengths differ or remote quit length not specified", "MAXQUIT", MAXQUIT},
173                         {"Maximum topic lengths differ or remote topic length not specified", "MAXTOPIC", MAXTOPIC},
174                         {"Maximum kick lengths differ or remote kick length not specified", "MAXKICK", MAXKICK},
175                         {"Maximum GECOS (fullname) lengths differ or remote GECOS length not specified", "MAXGECOS", MAXGECOS},
176                         {"Maximum awaymessage lengths differ or remote awaymessage length not specified", "MAXAWAY", MAXAWAY},
177                         {"", "", 0}
178                 };
179
180                 if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support))))
181                         reason = "We don't both support linking to IPV6 servers";
182                 if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support))
183                         reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers";
184                 if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
185                 {
186                         if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
187                                 reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
188                         else
189                                 reason = "Protocol version not specified";
190                 }
191
192                 if(this->CapKeys.find("PREFIX") != this->CapKeys.end() && this->CapKeys.find("PREFIX")->second != this->Instance->Modes->BuildPrefixes())
193                         reason = "One or more of the prefixes on the remote server are invalid on this server.";
194
195                 if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
196                         reason = "We don't both have halfop support enabled/disabled identically";
197
198                 for (int x = 0; valid_capab[x].size; ++x)
199                 {
200                         if (((this->CapKeys.find(valid_capab[x].key) == this->CapKeys.end()) || ((this->CapKeys.find(valid_capab[x].key) != this->CapKeys.end()) &&
201                                                  (this->CapKeys.find(valid_capab[x].key)->second != ConvToStr(valid_capab[x].size)))))
202                                 reason = valid_capab[x].reason;
203                 }
204         
205                 /* Challenge response, store their challenge for our password */
206                 std::map<std::string,std::string>::iterator n = this->CapKeys.find("CHALLENGE");
207                 if (Utils->ChallengeResponse && (n != this->CapKeys.end()) && (Instance->Modules->Find("m_sha256.so")))
208                 {
209                         /* Challenge-response is on now */
210                         this->SetTheirChallenge(n->second);
211                         if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING))
212                         {
213                                 this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(OutboundPass, this->GetTheirChallenge())+" 0 "+
214                                                 Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
215                         }
216                 }
217                 else
218                 {
219                         /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
220                         if (this->LinkState == CONNECTING)
221                                 this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+OutboundPass+" 0 "+Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
222                 }
223
224                 if (reason.length())
225                 {
226                         this->SendError("CAPAB negotiation failed: "+reason);
227                         return false;
228                 }
229         }
230         else if ((params[0] == "MODULES") && (params.size() == 2))
231         {
232                 if (!this->ModuleList.length())
233                 {
234                         this->ModuleList.append(params[1]);
235                 }
236                 else
237                 {
238                         this->ModuleList.append(",");
239                         this->ModuleList.append(params[1]);
240                 }
241         }
242
243         else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
244         {
245                 irc::tokenstream capabs(params[1]);
246                 std::string item;
247                 bool more = true;
248                 while ((more = capabs.GetToken(item)))
249                 {
250                         /* Process each key/value pair */
251                         std::string::size_type equals = item.rfind('=');
252                         if (equals != std::string::npos)
253                         {
254                                 std::string var = item.substr(0, equals);
255                                 std::string value = item.substr(equals+1, item.length());
256                                 CapKeys[var] = value;
257                         }
258                 }
259         }
260         return true;
261 }
262