anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
m_httpd.cpp (11873B)
1 /* 2 * 3 * (C) 2003-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 */ 8 9 #include "module.h" 10 #include "modules/httpd.h" 11 #include "modules/ssl.h" 12 13 static Anope::string BuildDate() 14 { 15 char timebuf[64]; 16 struct tm *tm = localtime(&Anope::CurTime); 17 strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", tm); 18 return timebuf; 19 } 20 21 static Anope::string GetStatusFromCode(HTTPError err) 22 { 23 switch (err) 24 { 25 case HTTP_ERROR_OK: 26 return "200 OK"; 27 case HTTP_FOUND: 28 return "302 Found"; 29 case HTTP_BAD_REQUEST: 30 return "400 Bad Request"; 31 case HTTP_PAGE_NOT_FOUND: 32 return "404 Not Found"; 33 case HTTP_NOT_SUPPORTED: 34 return "505 HTTP Version Not Supported"; 35 } 36 37 return "501 Not Implemented"; 38 } 39 40 class MyHTTPClient : public HTTPClient 41 { 42 HTTPProvider *provider; 43 HTTPMessage message; 44 bool header_done, served; 45 Anope::string page_name; 46 Reference<HTTPPage> page; 47 Anope::string ip; 48 49 unsigned content_length; 50 51 enum 52 { 53 ACTION_NONE, 54 ACTION_GET, 55 ACTION_POST 56 } action; 57 58 void Serve() 59 { 60 if (this->served) 61 return; 62 this->served = true; 63 64 if (!this->page) 65 { 66 this->SendError(HTTP_PAGE_NOT_FOUND, "Page not found"); 67 return; 68 } 69 70 if (std::find(this->provider->ext_ips.begin(), this->provider->ext_ips.end(), this->ip) != this->provider->ext_ips.end()) 71 { 72 for (unsigned i = 0; i < this->provider->ext_headers.size(); ++i) 73 { 74 const Anope::string &token = this->provider->ext_headers[i]; 75 76 if (this->message.headers.count(token)) 77 { 78 this->ip = this->message.headers[token]; 79 Log(LOG_DEBUG, "httpd") << "m_httpd: IP for connection " << this->GetFD() << " changed to " << this->ip; 80 break; 81 } 82 } 83 } 84 85 Log(LOG_DEBUG, "httpd") << "m_httpd: Serving page " << this->page_name << " to " << this->ip; 86 87 HTTPReply reply; 88 reply.content_type = this->page->GetContentType(); 89 90 if (this->page->OnRequest(this->provider, this->page_name, this, this->message, reply)) 91 this->SendReply(&reply); 92 } 93 94 public: 95 time_t created; 96 97 MyHTTPClient(HTTPProvider *l, int f, const sockaddrs &a) : Socket(f, l->IsIPv6()), HTTPClient(l, f, a), provider(l), header_done(false), served(false), ip(a.addr()), content_length(0), action(ACTION_NONE), created(Anope::CurTime) 98 { 99 Log(LOG_DEBUG, "httpd") << "Accepted connection " << f << " from " << a.addr(); 100 } 101 102 ~MyHTTPClient() 103 { 104 Log(LOG_DEBUG, "httpd") << "Closing connection " << this->GetFD() << " from " << this->ip; 105 } 106 107 /* Close connection once all data is written */ 108 bool ProcessWrite() anope_override 109 { 110 return !BinarySocket::ProcessWrite() || this->write_buffer.empty() ? false : true; 111 } 112 113 const Anope::string GetIP() anope_override 114 { 115 return this->ip; 116 } 117 118 bool Read(const char *buffer, size_t l) anope_override 119 { 120 message.content.append(buffer, l); 121 122 for (size_t nl; !this->header_done && (nl = message.content.find('\n')) != Anope::string::npos;) 123 { 124 Anope::string token = message.content.substr(0, nl).trim(); 125 message.content = message.content.substr(nl + 1); 126 127 if (token.empty()) 128 this->header_done = true; 129 else 130 this->Read(token); 131 } 132 133 if (!this->header_done) 134 return true; 135 136 if (this->message.content.length() >= this->content_length) 137 { 138 sepstream sep(this->message.content, '&'); 139 Anope::string token; 140 141 while (sep.GetToken(token)) 142 { 143 size_t sz = token.find('='); 144 if (sz == Anope::string::npos || !sz || sz + 1 >= token.length()) 145 continue; 146 this->message.post_data[token.substr(0, sz)] = HTTPUtils::URLDecode(token.substr(sz + 1)); 147 Log(LOG_DEBUG_2) << "HTTP POST from " << this->clientaddr.addr() << ": " << token.substr(0, sz) << ": " << this->message.post_data[token.substr(0, sz)]; 148 } 149 150 this->Serve(); 151 } 152 153 return true; 154 } 155 156 bool Read(const Anope::string &buf) 157 { 158 Log(LOG_DEBUG_2) << "HTTP from " << this->clientaddr.addr() << ": " << buf; 159 160 if (this->action == ACTION_NONE) 161 { 162 std::vector<Anope::string> params; 163 spacesepstream(buf).GetTokens(params); 164 165 if (params.empty() || (params[0] != "GET" && params[0] != "POST")) 166 { 167 this->SendError(HTTP_BAD_REQUEST, "Unknown operation"); 168 return true; 169 } 170 171 if (params.size() != 3) 172 { 173 this->SendError(HTTP_BAD_REQUEST, "Invalid parameters"); 174 return true; 175 } 176 177 if (params[0] == "GET") 178 this->action = ACTION_GET; 179 else if (params[0] == "POST") 180 this->action = ACTION_POST; 181 182 Anope::string targ = params[1]; 183 size_t q = targ.find('?'); 184 if (q != Anope::string::npos) 185 { 186 sepstream sep(targ.substr(q + 1), '&'); 187 targ = targ.substr(0, q); 188 189 Anope::string token; 190 while (sep.GetToken(token)) 191 { 192 size_t sz = token.find('='); 193 if (sz == Anope::string::npos || !sz || sz + 1 >= token.length()) 194 continue; 195 this->message.get_data[token.substr(0, sz)] = HTTPUtils::URLDecode(token.substr(sz + 1)); 196 } 197 } 198 199 this->page = this->provider->FindPage(targ); 200 this->page_name = targ; 201 } 202 else if (buf.find_ci("Cookie: ") == 0) 203 { 204 spacesepstream sep(buf.substr(8)); 205 Anope::string token; 206 207 while (sep.GetToken(token)) 208 { 209 size_t sz = token.find('='); 210 if (sz == Anope::string::npos || !sz || sz + 1 >= token.length()) 211 continue; 212 size_t end = token.length() - (sz + 1); 213 if (!sep.StreamEnd()) 214 --end; // Remove trailing ; 215 this->message.cookies[token.substr(0, sz)] = token.substr(sz + 1, end); 216 } 217 } 218 else if (buf.find_ci("Content-Length: ") == 0) 219 { 220 try 221 { 222 this->content_length = convertTo<unsigned>(buf.substr(16)); 223 } 224 catch (const ConvertException &ex) { } 225 } 226 else if (buf.find(':') != Anope::string::npos) 227 { 228 size_t sz = buf.find(':'); 229 if (sz + 2 < buf.length()) 230 this->message.headers[buf.substr(0, sz)] = buf.substr(sz + 2); 231 } 232 233 return true; 234 } 235 236 void SendError(HTTPError err, const Anope::string &msg) anope_override 237 { 238 HTTPReply h; 239 240 h.error = err; 241 242 h.Write(msg); 243 244 this->SendReply(&h); 245 } 246 247 void SendReply(HTTPReply *msg) anope_override 248 { 249 this->WriteClient("HTTP/1.1 " + GetStatusFromCode(msg->error)); 250 this->WriteClient("Date: " + BuildDate()); 251 this->WriteClient("Server: Anope-" + Anope::VersionShort()); 252 if (msg->content_type.empty()) 253 this->WriteClient("Content-Type: text/html"); 254 else 255 this->WriteClient("Content-Type: " + msg->content_type); 256 this->WriteClient("Content-Length: " + stringify(msg->length)); 257 258 for (unsigned i = 0; i < msg->cookies.size(); ++i) 259 { 260 Anope::string buf = "Set-Cookie:"; 261 262 for (HTTPReply::cookie::iterator it = msg->cookies[i].begin(), it_end = msg->cookies[i].end(); it != it_end; ++it) 263 buf += " " + it->first + "=" + it->second + ";"; 264 265 buf.erase(buf.length() - 1); 266 267 this->WriteClient(buf); 268 } 269 270 typedef std::map<Anope::string, Anope::string> map; 271 for (map::iterator it = msg->headers.begin(), it_end = msg->headers.end(); it != it_end; ++it) 272 this->WriteClient(it->first + ": " + it->second); 273 274 this->WriteClient("Connection: Close"); 275 this->WriteClient(""); 276 277 for (unsigned i = 0; i < msg->out.size(); ++i) 278 { 279 HTTPReply::Data* d = msg->out[i]; 280 281 this->Write(d->buf, d->len); 282 283 delete d; 284 } 285 286 msg->out.clear(); 287 } 288 }; 289 290 class MyHTTPProvider : public HTTPProvider, public Timer 291 { 292 int timeout; 293 std::map<Anope::string, HTTPPage *> pages; 294 std::list<Reference<MyHTTPClient> > clients; 295 296 public: 297 MyHTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, const int t, bool s) : Socket(-1, i.find(':') != Anope::string::npos), HTTPProvider(c, n, i, p, s), Timer(c, 10, Anope::CurTime, true), timeout(t) { } 298 299 void Tick(time_t) anope_override 300 { 301 while (!this->clients.empty()) 302 { 303 Reference<MyHTTPClient>& c = this->clients.front(); 304 if (c && c->created + this->timeout >= Anope::CurTime) 305 break; 306 307 delete c; 308 this->clients.pop_front(); 309 } 310 } 311 312 ClientSocket* OnAccept(int fd, const sockaddrs &addr) anope_override 313 { 314 MyHTTPClient *c = new MyHTTPClient(this, fd, addr); 315 this->clients.push_back(c); 316 return c; 317 } 318 319 bool RegisterPage(HTTPPage *page) anope_override 320 { 321 return this->pages.insert(std::make_pair(page->GetURL(), page)).second; 322 } 323 324 void UnregisterPage(HTTPPage *page) anope_override 325 { 326 this->pages.erase(page->GetURL()); 327 } 328 329 HTTPPage* FindPage(const Anope::string &pname) anope_override 330 { 331 if (this->pages.count(pname) == 0) 332 return NULL; 333 return this->pages[pname]; 334 } 335 }; 336 337 class HTTPD : public Module 338 { 339 ServiceReference<SSLService> sslref; 340 std::map<Anope::string, MyHTTPProvider *> providers; 341 public: 342 HTTPD(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), sslref("SSLService", "ssl") 343 { 344 345 } 346 347 ~HTTPD() 348 { 349 for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;) 350 { 351 Socket *s = it->second; 352 ++it; 353 354 if (dynamic_cast<MyHTTPProvider *>(s) || dynamic_cast<MyHTTPClient *>(s)) 355 delete s; 356 } 357 358 this->providers.clear(); 359 } 360 361 void OnReload(Configuration::Conf *config) anope_override 362 { 363 Configuration::Block *conf = config->GetModule(this); 364 std::set<Anope::string> existing; 365 366 for (int i = 0; i < conf->CountBlock("httpd"); ++i) 367 { 368 Configuration::Block *block = conf->GetBlock("httpd", i); 369 370 371 const Anope::string &hname = block->Get<const Anope::string>("name", "httpd/main"); 372 existing.insert(hname); 373 374 Anope::string ip = block->Get<const Anope::string>("ip"); 375 int port = block->Get<int>("port", "8080"); 376 int timeout = block->Get<int>("timeout", "30"); 377 bool ssl = block->Get<bool>("ssl", "no"); 378 Anope::string ext_ip = block->Get<const Anope::string>("extforward_ip"); 379 Anope::string ext_header = block->Get<const Anope::string>("extforward_header"); 380 381 if (ip.empty()) 382 { 383 Log(this) << "You must configure a bind IP for HTTP server " << hname; 384 continue; 385 } 386 else if (port <= 0 || port > 65535) 387 { 388 Log(this) << "You must configure a (valid) listen port for HTTP server " << hname; 389 continue; 390 } 391 392 MyHTTPProvider *p; 393 if (this->providers.count(hname) == 0) 394 { 395 try 396 { 397 p = new MyHTTPProvider(this, hname, ip, port, timeout, ssl); 398 if (ssl && sslref) 399 sslref->Init(p); 400 } 401 catch (const SocketException &ex) 402 { 403 Log(this) << "Unable to create HTTP server " << hname << ": " << ex.GetReason(); 404 continue; 405 } 406 this->providers[hname] = p; 407 408 Log(this) << "Created HTTP server " << hname; 409 } 410 else 411 { 412 p = this->providers[hname]; 413 414 if (p->GetIP() != ip || p->GetPort() != port) 415 { 416 delete p; 417 this->providers.erase(hname); 418 419 Log(this) << "Changing HTTP server " << hname << " to " << ip << ":" << port; 420 421 try 422 { 423 p = new MyHTTPProvider(this, hname, ip, port, timeout, ssl); 424 if (ssl && sslref) 425 sslref->Init(p); 426 } 427 catch (const SocketException &ex) 428 { 429 Log(this) << "Unable to create HTTP server " << hname << ": " << ex.GetReason(); 430 continue; 431 } 432 433 this->providers[hname] = p; 434 } 435 } 436 437 438 spacesepstream(ext_ip).GetTokens(p->ext_ips); 439 spacesepstream(ext_header).GetTokens(p->ext_headers); 440 } 441 442 for (std::map<Anope::string, MyHTTPProvider *>::iterator it = this->providers.begin(), it_end = this->providers.end(); it != it_end;) 443 { 444 HTTPProvider *p = it->second; 445 ++it; 446 447 if (existing.count(p->name) == 0) 448 { 449 Log(this) << "Removing HTTP server " << p->name; 450 this->providers.erase(p->name); 451 delete p; 452 } 453 } 454 } 455 456 void OnModuleLoad(User *u, Module *m) anope_override 457 { 458 for (std::map<Anope::string, MyHTTPProvider *>::iterator it = this->providers.begin(), it_end = this->providers.end(); it != it_end; ++it) 459 { 460 MyHTTPProvider *p = it->second; 461 462 if (p->IsSSL() && sslref) 463 try 464 { 465 sslref->Init(p); 466 } 467 catch (const CoreException &) { } // Throws on reinitialization 468 } 469 } 470 }; 471 472 MODULE_INIT(HTTPD)