1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS test application 4 * FILE: apps/net/netreg/netreg.cpp 5 * PURPOSE: HTTP Registry Server 6 * PROGRAMMERS: Art Yerkes (arty@users.sf.net) 7 * REVISIONS: 8 * 01-17-2005 arty -- initial 9 */ 10 #include <windows.h> 11 #include <stdlib.h> 12 #include <stdio.h> 13 #include <map> 14 #include <string> 15 #include <sstream> 16 #include <iomanip> 17 18 using std::hex; 19 using std::setw; 20 using std::setfill; 21 using std::map; 22 using std::string; 23 using std::ostringstream; 24 25 const char *root_entries[] = { 26 "HKEY_LOCAL_MACHINE", 27 "HKEY_CURRENT_USER", 28 "HKEY_CLASSES_ROOT", 29 "HKEY_CURRENT_CONFIG", 30 "HKEY_USERS", 31 0 32 }; 33 34 const HKEY root_handles[] = { 35 HKEY_LOCAL_MACHINE, 36 HKEY_CURRENT_USER, 37 HKEY_CLASSES_ROOT, 38 HKEY_CURRENT_CONFIG, 39 HKEY_USERS 40 }; 41 42 class RequestHandler { 43 public: 44 RequestHandler( SOCKET s ) : socket( s ), state( NO_REQUEST_YET ) {} 45 ~RequestHandler() { closesocket( socket ); } 46 void RecvData( string input ) { 47 full_input += input; 48 if( full_input.find( "\r\n\r\n" ) != string::npos ) { 49 // Full request received... 50 size_t space_pos = full_input.find( ' ' ); 51 if( space_pos == string::npos ) { state = SHOULD_DIE; return; } 52 string method = full_input.substr( 0, space_pos ); 53 if( method != "GET" ) { state = SHOULD_DIE; return; } 54 space_pos++; 55 if( full_input[space_pos] != '/' ) { state = SHOULD_DIE; return; } 56 space_pos++; 57 string reg_key_and_remainder = 58 full_input.substr( space_pos, full_input.size() - space_pos ); 59 space_pos = reg_key_and_remainder.find( ' ' ); 60 if( space_pos == string::npos ) { state = SHOULD_DIE; return; } 61 string reg_key_name = reg_key_and_remainder.substr( 0, space_pos ); 62 process_request( urldec( reg_key_name ) ); 63 state = REQUEST_RECVD_SENDING_REPLY; 64 } 65 } 66 void OkToSend() { 67 int rv = send( socket, 68 remaining_output.c_str(), 69 remaining_output.size(), 0 ); 70 if( rv < 0 ) { 71 state = SHOULD_DIE; 72 return; 73 } else { 74 remaining_output = 75 remaining_output.substr( rv, remaining_output.size() - rv ); 76 if( remaining_output.size() == 0 ) { 77 state = SHOULD_DIE; 78 } 79 } 80 } 81 82 SOCKET GetSocket() const { return socket; } 83 84 bool ShouldDie() const { 85 return state == SHOULD_DIE; 86 } 87 88 bool WantPollout() const { 89 return state == REQUEST_RECVD_SENDING_REPLY; 90 } 91 92 93 private: 94 string urlenc( string in ) { 95 ostringstream out; 96 97 for( string::iterator i = in.begin(); 98 i != in.end(); 99 i++ ) { 100 if( isalnum( *i ) || *i == '/' ) 101 out << *i; 102 else { 103 char minibuf[10]; 104 sprintf( minibuf, "%02x", *i ); 105 out << "%" << minibuf; 106 } 107 } 108 109 return out.str(); 110 } 111 112 string urldec( string in ) { 113 string out; 114 115 for( string::iterator i = in.begin(); 116 i != in.end(); 117 i++ ) { 118 if( *i == '%' ) { 119 char buf[3]; 120 int res = ' '; 121 122 i++; 123 if( i != in.end() ) { 124 buf[0] = *i; 125 i++; 126 if( i != in.end() ) { 127 buf[1] = *i; 128 buf[2] = 0; 129 sscanf( buf, "%x", &res ); 130 fprintf( stderr, "Interpreting %c%c as %02x\n", 131 buf[0], buf[1], 132 res ); 133 out += (char)res; 134 } 135 } 136 } else out += *i; 137 } 138 139 return out; 140 } 141 142 string dump_one_line( const char *data, int llen, int len, int addr ) { 143 ostringstream out; 144 int i; 145 146 out << setw( 8 ) << setfill( '0' ) << hex << addr << ": "; 147 148 for( i = 0; i < llen; i++ ) { 149 if( i < len ) out << setw( 2 ) << setfill( '0' ) << hex << 150 (data[i] & 0xff) << " "; 151 else out << " "; 152 } 153 154 out << " : "; 155 156 for( i = 0; i < llen; i++ ) { 157 if( i < len && i < llen && 158 data[i] >= ' ' && data[i] < 0x7f ) out << data[i]; else out << '.'; 159 } 160 161 out << "\n"; 162 163 return out.str(); 164 } 165 166 string bindump( const char *data, int len ) { 167 const char *end = data + len; 168 string out; 169 int addr = 0; 170 171 out += "<pre>"; 172 173 while( data < end ) { 174 out += dump_one_line( data, 16, end - data, addr ); 175 addr += 16; 176 data += 16; 177 } 178 179 out += "</pre>"; 180 181 return out; 182 } 183 184 string present_value( DWORD type, const char *data, DWORD len ) { 185 switch( type ) { 186 default: 187 return bindump( data, len ); 188 } 189 } 190 191 void process_valid_request( HKEY open_reg_key, string key_name ) { 192 size_t ending_slash; 193 string up_level; 194 ostringstream text_out; 195 196 DWORD num_sub_keys; 197 DWORD max_subkey_len; 198 DWORD num_values; 199 DWORD max_value_name_len; 200 DWORD max_value_len; 201 202 char *value_name_buf; 203 char *value_buf; 204 char *key_name_buf; 205 206 if( RegQueryInfoKey( open_reg_key, 207 NULL, 208 NULL, 209 NULL, 210 &num_sub_keys, 211 &max_subkey_len, 212 NULL, 213 &num_values, 214 &max_value_name_len, 215 &max_value_len, 216 NULL, 217 NULL ) != ERROR_SUCCESS ) { 218 process_invalid_request( key_name ); 219 return; 220 } 221 222 value_name_buf = new char [max_value_name_len+1]; 223 value_buf = new char [max_value_len+1]; 224 key_name_buf = new char [max_subkey_len+1]; 225 226 ending_slash = key_name.rfind( '/' ); 227 if( ending_slash != string::npos ) 228 up_level = key_name.substr( 0, ending_slash ); 229 230 text_out << "HTTP/1.0 200 OK\r\n" 231 << "Content-Type: text/html\r\n" 232 << "\r\n" 233 << "<html><head><title>Registry Key `" 234 << key_name 235 << "'</title></head><body>\r\n" 236 << "<h1>Registry Key `" << key_name << "'</h1>\r\n" 237 << "<a href='/" << urlenc(up_level) 238 << "'>(Up one level)</a><p>\r\n" 239 << "<h2>Subkeys:</h2><table border='1'>\r\n"; 240 241 DWORD which_index; 242 DWORD key_name_size; 243 244 for( which_index = 0; which_index < num_sub_keys; which_index++ ) { 245 key_name_size = max_subkey_len+1; 246 RegEnumKeyEx( open_reg_key, 247 which_index, 248 key_name_buf, 249 &key_name_size, 250 NULL, 251 NULL, 252 NULL, 253 NULL ); 254 text_out << "<tr><td><a href='/" << urlenc(key_name) << "/" 255 << urlenc(string(key_name_buf,key_name_size)) << "'>" 256 << string(key_name_buf,key_name_size) 257 << "</a></td></tr>\r\n"; 258 } 259 260 text_out << "</table><h2>Values:</h2><table border='1'>\r\n"; 261 262 DWORD value_name_size; 263 DWORD value_data_size; 264 DWORD value_type; 265 266 for( which_index = 0; which_index < num_values; which_index++ ) { 267 value_name_size = max_value_name_len+1; 268 value_data_size = max_value_len+1; 269 270 RegEnumValue( open_reg_key, 271 which_index, 272 value_name_buf, 273 &value_name_size, 274 NULL, 275 &value_type, 276 (BYTE *)value_buf, 277 &value_data_size ); 278 279 text_out << "<tr><td><b>" << string(value_name_buf,value_name_size) 280 << "</b></td><td>" 281 << present_value( value_type, value_buf, value_data_size ) 282 << "</td></tr>"; 283 } 284 285 text_out << "</ul></body></html>\r\n"; 286 287 delete [] key_name_buf; 288 delete [] value_name_buf; 289 delete [] value_buf; 290 291 remaining_output = text_out.str(); 292 } 293 294 void process_invalid_request( string reg_key ) { 295 ostringstream text_out; 296 text_out << "HTTP/1.0 404 Not Found\r\n" 297 << "Content-Type: text/html\r\n" 298 << "\r\n" 299 << "<html><head><title>Can't find registry key `" 300 << reg_key 301 << "'</title></head><body>\r\n" 302 << "<H1>Can't find registry key `" 303 << reg_key 304 << "'</H1>\r\n" 305 << "The registry key doesn't exist in the local registry.\r\n" 306 << "</body></html>\r\n"; 307 308 remaining_output = text_out.str(); 309 } 310 311 void process_root_request() { 312 ostringstream text_out; 313 int i; 314 315 text_out << "HTTP/1.0 200 OK\r\n" 316 << "Content-Type: text/html\r\n" 317 << "\r\n" 318 << "<html><head><title>Registry Browser</title></head>\r\n" 319 << "<body>\r\n" 320 << "<H1>Registry Browser</H1>" 321 << "You can use this interface to browse the registry." 322 << "You will be presented with one registry key at a time and " 323 << "the decendents.\r\n" 324 << "<h2>Root Level</h2>\r\n" 325 << "Subkeys:<ul>\r\n"; 326 327 for( i = 0; root_entries[i]; i++ ) 328 text_out << "<li>" 329 << "<a href='/" << urlenc(root_entries[i]) 330 << "'>" << root_entries[i] 331 << "</a></li>\r\n"; 332 333 text_out << "</ul></body></html>\r\n"; 334 335 remaining_output = text_out.str(); 336 } 337 338 void process_request( string reg_key ) { 339 int i; 340 bool is_predefined_key = true; 341 342 if( reg_key == "" ) { process_root_request(); return; } 343 HKEY hRegKey = 0; 344 345 // Parse the key name... 346 size_t slash = reg_key.find( '/' ); 347 string reg_initial = ""; 348 349 if( slash == string::npos ) // A root key... 350 reg_initial = reg_key; 351 else // Any other key 352 reg_initial = reg_key.substr( 0, slash ); 353 354 fprintf( stderr, "reg_init = %s, reg_key = %s\n", 355 reg_initial.c_str(), 356 reg_key.c_str() ); 357 358 for( i = 0; root_entries[i]; i++ ) 359 if( reg_initial == root_entries[i] ) hRegKey = root_handles[i]; 360 361 if( hRegKey != 0 && reg_initial != reg_key ) { 362 size_t start_of_reg_path = reg_initial.size() + 1; 363 string reg_path = reg_key.substr( start_of_reg_path, 364 reg_key.size() - start_of_reg_path ); 365 366 string reg_open_path = reg_path; 367 do { 368 slash = reg_open_path.find( '/' ); 369 string reg_single_key = reg_open_path; 370 371 if( slash != string::npos ) { 372 reg_single_key = reg_open_path.substr( 0, slash ); 373 reg_open_path = reg_open_path.substr( slash+1, 374 reg_open_path.size() ); 375 } 376 377 HKEY oldKey = hRegKey; 378 379 fprintf( stderr, "Opening %s\n", reg_single_key.c_str() ); 380 381 if( RegOpenKey( hRegKey, reg_single_key.c_str(), &hRegKey ) != 382 ERROR_SUCCESS ) { 383 hRegKey = 0; 384 break; 385 } else RegCloseKey( oldKey ); 386 387 is_predefined_key = false; 388 } while( slash != string::npos ); 389 } 390 391 if( hRegKey == 0 ) process_invalid_request( reg_key ); 392 else { 393 process_valid_request( hRegKey, reg_key ); 394 if( !is_predefined_key ) RegCloseKey( hRegKey ); 395 } 396 } 397 398 typedef enum _RHState { 399 NO_REQUEST_YET, 400 REQUEST_RECVD_SENDING_REPLY, 401 SHOULD_DIE 402 } RHState; 403 404 string full_input; 405 string remaining_output; 406 SOCKET socket; 407 RHState state; 408 }; 409 410 SOCKET make_listening_socket( int port ) { 411 struct sockaddr_in sa; 412 413 ZeroMemory( &sa, sizeof( sa ) ); 414 415 sa.sin_family = PF_INET; 416 sa.sin_port = ntohs( port ); 417 418 fprintf( stderr, "Creating the listener\n" ); 419 SOCKET l = socket( PF_INET, SOCK_STREAM, 0 ); 420 fprintf( stderr, "Socket %Ix\n", l ); 421 422 if( l == INVALID_SOCKET ) return l; 423 if( bind( l, (struct sockaddr *)&sa, sizeof( sa ) ) < 0 ) { 424 fprintf( stderr, "Bad response from bind: %d\n", WSAGetLastError() ); 425 closesocket( l ); return INVALID_SOCKET; 426 } 427 if( listen( l, 5 ) < 0 ) { 428 fprintf( stderr, "Listening: %d\n", WSAGetLastError() ); 429 closesocket( l ); 430 return INVALID_SOCKET; 431 } 432 433 return l; 434 } 435 436 int main( int argc, char **argv ) { 437 WSADATA wdWinsockData; 438 map<SOCKET,RequestHandler *> requests; 439 fd_set pollin,pollout,pollerr; 440 SOCKET listen_socket; 441 int i; 442 int port_to_listen = 80; 443 unsigned int active_fds = 0; 444 445 for( i = 1; i < argc; i++ ) { 446 if( string( "-p" ) == argv[i] ) { 447 i++; 448 if( i < argc ) port_to_listen = atoi( argv[i] ); 449 } 450 } 451 452 WSAStartup( 0x0101, &wdWinsockData ); 453 454 listen_socket = make_listening_socket( port_to_listen ); 455 if( listen_socket == INVALID_SOCKET ) return 1; 456 457 while( true ) { 458 FD_ZERO( &pollin ); 459 FD_ZERO( &pollout ); 460 FD_ZERO( &pollerr ); 461 active_fds = listen_socket + 1; 462 463 for( std::map<SOCKET,RequestHandler *>::iterator i = requests.begin(); 464 i != requests.end(); 465 i++ ) { 466 if( i->second->ShouldDie() ) { 467 delete i->second; 468 requests.erase( i ); 469 i = requests.begin(); 470 break; 471 } 472 473 FD_SET(i->first,&pollin); 474 FD_SET(i->first,&pollerr); 475 476 if( i->first > active_fds ) active_fds = i->first + 1; 477 478 if( i->second->WantPollout() ) FD_SET(i->first,&pollout); 479 } 480 481 FD_SET(listen_socket,&pollin); 482 483 active_fds = select( active_fds, &pollin, &pollout, &pollerr, NULL ); 484 485 if( active_fds > 0 ) { 486 if( FD_ISSET(listen_socket,&pollin) ) { 487 SOCKET ns = accept( listen_socket, NULL, NULL ); 488 if( ns != INVALID_SOCKET ) { 489 requests.insert( std::make_pair( ns, new RequestHandler( ns ) ) ); 490 } 491 } 492 493 for( std::map<SOCKET,RequestHandler *>::iterator i = requests.begin(); 494 i != requests.end(); 495 i++ ) { 496 if( FD_ISSET(i->first,&pollin) ) { 497 char inbuf[1024]; 498 int rv = recv(i->first,inbuf,1024,0); 499 if( rv < 0 ) { 500 delete i->second; 501 requests.erase( i ); 502 i = requests.begin(); 503 break; 504 } else i->second->RecvData( string( inbuf, rv ) ); 505 } 506 if( FD_ISSET(i->first,&pollout) ) { 507 i->second->OkToSend(); 508 } 509 if( FD_ISSET(i->first,&pollerr) ) { 510 delete i->second; 511 requests.erase( i ); 512 i = requests.begin(); 513 break; 514 } 515 } 516 } 517 } 518 519 WSACleanup(); 520 } 521