// The contents of this file are subject to the BOINC Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://boinc.berkeley.edu/license_1.0.txt // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is the Berkeley Open Infrastructure for Network Computing. // // The Initial Developer of the Original Code is the SETI@home project. // Portions created by the SETI@home project are Copyright (C) 2002 // University of California at Berkeley. All Rights Reserved. // // Contributor(s): // #ifdef _WIN32 #include "boinc_win.h" #endif #ifndef _WIN32 #include #include #include #include #include #include #include #include #include #endif #include "util.h" #include "error_numbers.h" #include "parse.h" #include "file_names.h" #include "client_msgs.h" #include "client_state.h" using std::string; using std::vector; #if defined(_WIN32) typedef int socklen_t; #elif defined ( __APPLE__) typedef int32_t socklen_t; #elif !GETSOCKOPT_SOCKLEN_T #ifndef socklen_t typedef size_t socklen_t; #endif #endif static void boinc_close_socket(int sock) { #ifdef _WIN32 closesocket(sock); #else close(sock); #endif } GUI_RPC_CONN::GUI_RPC_CONN(int s) { sock = s; } GUI_RPC_CONN::~GUI_RPC_CONN() { boinc_close_socket(sock); } static void handle_get_project_status(MIOFILE& fout) { unsigned int i; fout.printf("\n"); for (i=0; iwrite_state(fout, true); } fout.printf("\n"); } static void handle_get_disk_usage(MIOFILE& fout) { unsigned int i; double size; fout.printf("\n"); for (i=0; i\n" " %s\n" " %f\n" "\n", p->master_url, size ); } fout.printf("\n"); } static PROJECT* get_project(char* buf, MIOFILE& fout) { string url; if (!parse_str(buf, "", url)) { fout.printf("Missing project URL\n"); return 0; } PROJECT* p = gstate.lookup_project(url.c_str()); if (!p) { fout.printf("No such project\n"); return 0 ; } return p; } static void handle_result_show_graphics(char* buf, MIOFILE& fout) { string result_name; ACTIVE_TASK* atp; int mode; if (match_tag(buf, "")) { mode = MODE_FULLSCREEN; } else { mode = MODE_WINDOW; } if (parse_str(buf, "", result_name)) { PROJECT* p = get_project(buf, fout); if (!p) { fout.printf("No such project\n"); return; } RESULT* rp = gstate.lookup_result(p, result_name.c_str()); if (!rp) { fout.printf("No such result\n"); return; } atp = gstate.lookup_active_task_by_result(rp); if (!atp || atp->scheduler_state != CPU_SCHED_SCHEDULED) { fout.printf("Result not active\n"); return; } atp->request_graphics_mode(mode); } else { for (unsigned int i=0; ischeduler_state != CPU_SCHED_SCHEDULED) continue; atp->request_graphics_mode(mode); } } fout.printf("\n"); } static void handle_project_op(char* buf, MIOFILE& fout, char* op) { PROJECT* p = get_project(buf, fout); if (!p) { fout.printf("no such project\n"); return; } if (!strcmp(op, "reset")) { gstate.reset_project(p); } else if (!strcmp(op, "suspend")) { p->suspended_via_gui = true; } else if (!strcmp(op, "resume")) { p->suspended_via_gui = false; } else if (!strcmp(op, "detach")) { gstate.detach_project(p); } else if (!strcmp(op, "update")) { p->sched_rpc_pending = true; p->min_rpc_time = 0; } gstate.must_schedule_cpus = true; fout.printf("\n"); } static void handle_project_attach(char* buf, MIOFILE& fout) { string url, authenticator; if (!parse_str(buf, "", url)) { fout.printf("Missing URL\n"); return; } if (!parse_str(buf, "", authenticator)) { fout.printf("Missing authenticator\n"); return; } gstate.add_project(url.c_str(), authenticator.c_str()); fout.printf("\n"); } static void handle_set_run_mode(char* buf, MIOFILE& fout) { if (match_tag(buf, "Missing mode\n"); return; } fout.printf("\n"); } static void handle_get_run_mode(char* , MIOFILE& fout) { fout.printf("\n"); switch (gstate.user_run_request) { case USER_RUN_REQUEST_ALWAYS: fout.printf("\n"); break; case USER_RUN_REQUEST_NEVER: fout.printf("\n"); break; case USER_RUN_REQUEST_AUTO: fout.printf("\n"); break; default: fout.printf("Unknown run mode\n"); } fout.printf("\n"); } static void handle_set_network_mode(char* buf, MIOFILE& fout) { if (match_tag(buf, "Missing mode\n"); return; } fout.printf("\n"); } static void handle_get_network_mode(char* , MIOFILE& fout) { fout.printf("\n"); switch (gstate.user_network_request) { case USER_RUN_REQUEST_ALWAYS: fout.printf("\n"); break; case USER_RUN_REQUEST_NEVER: fout.printf("\n"); break; default: fout.printf("Unknown network mode\n"); } fout.printf("\n"); } static void handle_run_benchmarks(char* , MIOFILE& fout) { gstate.start_cpu_benchmarks(); fout.printf("\n"); } static void handle_set_proxy_settings(char* buf, MIOFILE& fout) { MIOFILE in; in.init_buf(buf); gstate.proxy_info.parse(in); gstate.set_client_state_dirty("Set proxy settings RPC"); fout.printf("\n"); } static void handle_get_proxy_settings(char* , MIOFILE& fout) { gstate.proxy_info.write(fout); } // params: // [ n ] // return only msgs with seqno > n; if absent or zero, return all // void handle_get_messages(char* buf, MIOFILE& fout) { int seqno=0, i, j; unsigned int k; MESSAGE_DESC* mdp; bool found=false; parse_int(buf, "", seqno); j = message_descs.size()-1; for (k=0; kseqno <= seqno) { found = true; j = k-1; break; } } fout.printf("\n"); for (i=j; i>=0; i--) { mdp = message_descs[i]; fout.printf( "\n" " %d\n" " %d\n" " \n%s\n\n" " \n", mdp->priority, mdp->seqno, mdp->message.c_str(), mdp->timestamp ); if (mdp->project) { fout.printf( " %s\n", mdp->project->get_project_name() ); } fout.printf("\n"); } fout.printf("\n"); } // // XXX // XXX // static void handle_file_transfer_op(char* buf, MIOFILE& fout, char* op) { string filename; PROJECT* p = get_project(buf, fout); if (!p) { fout.printf("No such project\n"); return; } if (!parse_str(buf, "", filename)) { fout.printf("Missing filename\n"); return; } FILE_INFO* f = gstate.lookup_file_info(p, filename.c_str()); if (!f) { fout.printf("No such file\n"); return; } if (!f->pers_file_xfer) { fout.printf("No such transfer waiting\n"); return; } if (!strcmp(op, "retry")) { f->pers_file_xfer->next_request_time = 0; } else if (!strcmp(op, "abort")) { f->pers_file_xfer->abort(); } else { fout.printf("unknown op\n"); return; } fout.printf("\n"); } static void handle_result_op(char* buf, MIOFILE& fout, char* op) { RESULT* rp; char result_name[256]; ACTIVE_TASK* atp; PROJECT* p = get_project(buf, fout); if (!p) { fout.printf("No such project\n"); return; } if (!parse_str(buf, "", result_name, sizeof(result_name))) { fout.printf("Missing result name\n"); return; } rp = gstate.lookup_result(p, result_name); if (!rp) { fout.printf("no such result\n"); return; } atp = gstate.lookup_active_task_by_result(rp); if (!atp) { fout.printf("result is not active\n"); return; } if (!strcmp(op, "abort")) { atp->abort_task("aborted via GUI RPC"); } else if (!strcmp(op, "suspend")) { atp->suspended_via_gui = true; } else if (!strcmp(op, "resume")) { atp->suspended_via_gui = false; } gstate.must_schedule_cpus = true; fout.printf("\n"); } static void handle_get_host_info(char*, MIOFILE& fout) { gstate.host_info.write(fout); } int GUI_RPC_CONN::handle_rpc() { char request_msg[1024]; int n; MIOFILE mf; MFILE m; char* p; int client_version; mf.init_mfile(&m); SCOPE_MSG_LOG scope_messages(log_messages, CLIENT_MSG_LOG::DEBUG_GUIRPC); // read the request message in one read() // so that the core client won't hang because // of malformed request msgs // #ifdef _WIN32 n = recv(sock, request_msg, 1024, 0); #else n = read(sock, request_msg, 1024); #endif if (n <= 0) return -1; request_msg[n] = 0; scope_messages.printf("GUI RPC Command = '%s'\n", request_msg); // get client version. not used for now // parse_int(request_msg, "", client_version); mf.printf( "\n" "%d\n", gstate.version() ); if (match_tag(request_msg, "unrecognized op\n"); } mf.printf("\n\003"); m.get_buf(p, n); send(sock, p, n, 0); if (p) free(p); return 0; } int GUI_RPC_CONN_SET::get_allowed_hosts() { SCOPE_MSG_LOG scope_messages(log_messages, CLIENT_MSG_LOG::DEBUG_STATE); // add localhost allowed_remote_ip_addresses.push_back(0x7f000001); NET_XFER temp; // network address resolver is in this class int ipaddr; char buf[256]; // open file remote_hosts.cfg and read in the // allowed host list and resolve them to an ip address // FILE* f = fopen(REMOTEHOST_FILE_NAME, "r"); if (f != NULL) { scope_messages.printf( "GUI_RPC_CONN_SET::get_allowed_hosts(): found allowed hosts list\n" ); // read in each line, if it is not a comment // then resolve the address and add to our // allowed list memset(buf,0,sizeof(buf)); while (fgets(buf, 256, f) != NULL) { strip_whitespace(buf); if (!(buf[0] =='#' || buf[0] == ';') && strlen(buf) > 0 ) { // resolve and add if (temp.get_ip_addr(buf, ipaddr) == 0) { allowed_remote_ip_addresses.push_back((int)ntohl(ipaddr)); } } } fclose(f); } return 0; } int GUI_RPC_CONN_SET::insert(GUI_RPC_CONN* p) { gui_rpcs.push_back(p); return 0; } int GUI_RPC_CONN_SET::init() { sockaddr_in addr; int retval; // get list of hosts allowed to do GUI RPCs // get_allowed_hosts(); lsock = socket(AF_INET, SOCK_STREAM, 0); if (lsock < 0) { msg_printf(NULL, MSG_ERROR, "GUI RPC failed to create socket: %d\n", lsock ); return ERR_SOCKET; } addr.sin_family = AF_INET; addr.sin_port = htons(GUI_RPC_PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); int one = 1; setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char*)&one, 4); retval = bind(lsock, (const sockaddr*)(&addr), sizeof(addr)); if (retval) { msg_printf(NULL, MSG_ERROR, "GUI RPC bind failed: %d\n", retval); boinc_close_socket(lsock); lsock = -1; return ERR_BIND; } retval = listen(lsock, 999); if (retval) { msg_printf(NULL, MSG_ERROR, "GUI RPC listen failed: %d\n", retval); boinc_close_socket(lsock); lsock = -1; return ERR_LISTEN; } return 0; } bool GUI_RPC_CONN_SET::poll(double) { int n = 0; unsigned int i; fd_set read_fds, error_fds; int sock, retval; vector::iterator iter; GUI_RPC_CONN* gr; struct timeval tv; if (lsock < 0) return false; FD_ZERO(&read_fds); FD_ZERO(&error_fds); FD_SET(lsock, &read_fds); for (i=0; isock, &read_fds); FD_SET(gr->sock, &error_fds); } memset(&tv, 0, sizeof(tv)); n = select(FD_SETSIZE, &read_fds, 0, &error_fds, &tv); if (FD_ISSET(lsock, &read_fds)) { struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); sock = accept(lsock, (struct sockaddr*)&addr, &addr_len); int peer_ip = (int) ntohl(addr.sin_addr.s_addr); // check list of allowed remote hosts bool allowed = false; vector::iterator remote_iter; remote_iter = allowed_remote_ip_addresses.begin(); while (remote_iter != allowed_remote_ip_addresses.end() ) { int remote_host = *remote_iter; if (peer_ip == remote_host) allowed = true; remote_iter++; } // accept the connection if: // 1) allow_remote_gui_rpc is set or // 2) client host is included in "remote_hosts" file or // 3) client is on localhost // if ( !(gstate.allow_remote_gui_rpc) && !(allowed)) { in_addr ia; ia.s_addr = htonl(peer_ip); msg_printf( NULL, MSG_ERROR, "GUI RPC request from non-allowed address %s\n", inet_ntoa(ia) ); boinc_close_socket(sock); } else { GUI_RPC_CONN* gr = new GUI_RPC_CONN(sock); insert(gr); } } iter = gui_rpcs.begin(); while (iter != gui_rpcs.end()) { gr = *iter; if (FD_ISSET(gr->sock, &error_fds)) { delete gr; gui_rpcs.erase(iter); } else { iter++; } } iter = gui_rpcs.begin(); while (iter != gui_rpcs.end()) { gr = *iter; if (FD_ISSET(gr->sock, &read_fds)) { retval = gr->handle_rpc(); if (retval) { delete gr; gui_rpcs.erase(iter); continue; } } iter++; } return (n > 0); }