📄 tcp_client.c++
字号:
// Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.// // This program is free software; you can redistribute it and/or modify it// under the terms of version 2 of the GNU General Public License as// published by the Free Software Foundation.//// This program is distributed in the hope that it would be useful, but// WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any// license provided herein, whether implied or otherwise, is limited to// this program in accordance with the express provisions of the GNU// General Public License. Patent licenses, if any, provided herein do not// apply to combinations of this program with other product or programs, or// any other product whatsoever. This program is distributed without any// warranty that the program is delivered free of the rightful claim of any// third person by way of infringement or the like. See the GNU General// Public License for more details.//// You should have received a copy of the GNU General Public License along// with this program; if not, write the Free Software Foundation, Inc., 59// Temple Place - Suite 330, Boston MA 02111-1307, USA.#include "TCP_Client.h"#include <assert.h>#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include "Cred.h"#include "Event.h"#include "Interest.h"#include "Log.h"#include "Scanner.h"#include "Listener.h"//////////////////////////////////////////////////////////////////////////////// Construction/destructionTCP_Client::TCP_Client(in_addr host, int fd, Cred &cr) : MxClient(host), cred(cr), my_scanner(NULL), conn(fd, input_handler, unblock_handler, this), insecure_compat_suggested(false){ assert(fd >= 0); // Set client's name. char namebuf[20]; sprintf(namebuf, "client %d", fd); name(namebuf); Log::debug("new connection from %s", name());}TCP_Client::~TCP_Client(){}//////////////////////////////////////////////////////////////////////////////// InputboolTCP_Client::input_handler(const char *msg, unsigned nbytes, void *closure){ TCP_Client *client = (TCP_Client *) closure; if (msg) { return client->input_msg(msg, nbytes); } else { Log::debug("lost connection from %s", client->name()); delete client; // NULL msg means connection closed. return true; }}boolTCP_Client::input_msg(const char *msg, int size){ // Parse the first message. // The first message is: // opcode, request number, uid, gid, file name. // If there's an error parsing the message, return false to cause // the connection to the misbehaving client to be closed. bool got_N_with_groups = false; char *p = (char *) msg, *q; char opcode = *p++; Request reqnum = strtol(p, &q, 10); if (p == q) { Log::error("bad message (no request id)"); return false; } p = q; uid_t uid = strtol(p, &q, 10); if (p == q) { Log::error("bad message (no uid)"); return false; } p = q; gid_t gid = strtol(p, &q, 10); if (p == q) { Log::error("bad message (no gid)"); return false; } p = q; // Advance past the space p++; char filename[PATH_MAX + 1]; int i; for (i = 0; *p; i++) { if (i >= PATH_MAX) { Log::error("%s path name too long (%d chars)", name(), i); return false; } filename[i] = *p++; } filename[i] = '\0'; if (i > 0 && filename[i - 1] == '\n') filename[i - 1] = '\0'; // strip the trailing newline p++; // Parse the second message, if any. // The second message is: // ngroups, group, group, group ... // This is a raging kludge. got_N_with_groups is how a client says // "create a UNIX domain socket for local communication with me." // You guessed that, right? // // The reason it's done this way, rather than by having fam listen // for connections from local clients on a unix domain socket in addition // to the internet domain socket, is that when fam is started by inetd, // the first client would have to connect on the internet domain socket // anyway to make inetd exec fam. if ((opcode == 'N') && (p < msg + size - 1)) got_N_with_groups = true; // If no Cred is set on the connection, that means we trust the uid // & gids supplied in the message. Cred msg_cred = cred; if (!msg_cred.is_valid()) { gid_t *grouplist = &gid; int ngroups = 1; if (p < msg + size - 1) { ngroups += strtol(p, &p, 10); int maxgroups = sysconf(_SC_NGROUPS_MAX); if (ngroups > maxgroups) { Log::info("message contained %i groups, but group list was" " truncated to %i because of _SC_NGROUPS_MAX", ngroups, maxgroups); ngroups = maxgroups; } grouplist = new gid_t[ngroups]; grouplist[0] = gid; for (int i = 1; i < ngroups; i++) { grouplist[i] = strtol(p, &q, 10); if (p == q) { Log::error("bad message (%d additional groups expected, %d found)", ngroups - 1, i); ngroups = i; break; } p = q; } } Cred c(uid, ngroups, grouplist, conn.get_fd()); msg_cred = c; if (grouplist != &gid) delete [] grouplist; } // Process the message. switch (opcode) { case 'W': // Monitor File { Log::debug("%s said: request %d monitor file \"%s\"", name(), reqnum, filename); monitor_file(reqnum, filename, msg_cred); break; } case 'M': // Monitor Directory Log::debug("%s said: request %d monitor dir \"%s\"", name(), reqnum, filename); monitor_dir(reqnum, filename, msg_cred); break; case 'C': // Cancel Log::debug("%s said: cancel request %d", name(), reqnum); cancel(reqnum); break; case 'S': // Suspend Log::debug("%s said: suspend request %d", name(), reqnum); MxClient::suspend(reqnum); break; case 'U': // Resume Log::debug("%s said: resume request %d", name(), reqnum); MxClient::resume(reqnum); break; case 'N': // Client Name Log::debug("%s said: %s is %s, and %s a unix domain socket", name(), name(), filename, (got_N_with_groups ? "wants" : "doesn't want")); if (*filename && strcmp(filename, "test")) name(filename); // set this client's name // Check to see whether this client wants to switch to a unix // domain socket. Create it owned by the uid the client says // they're running as. if (got_N_with_groups) Listener::create_local_client(*this, uid); break; // // Ignore these obsolete messages. // case 'D': case 'V': case 'E': break; default: Log::error("%s said unknown request '%c' ('\\%3o')", name(), opcode, opcode & 0377); return false; } return true;}//////////////////////////////////////////////////////////////////////////////// OutputvoidTCP_Client::unblock_handler(void *closure){ TCP_Client *client = (TCP_Client *) closure; // Continue scanner, if any. Scanner *scanner = client->my_scanner; if (scanner) { if (scanner->done()) client->my_scanner = NULL; else return; } // After scanner has run, scan more interests. Interest *ip; while (client->ready_for_events() && (ip = client->to_be_scanned.first())) { client->to_be_scanned.remove(ip); ip->scan(); } // Enable input if all enqueued work is done. if (client->ready_for_events()) client->conn.ready_for_input(true);}boolTCP_Client::ready_for_events(){ return conn.ready_for_output();}voidTCP_Client::enqueue_for_scan(Interest *ip){ if (!to_be_scanned.size()) conn.ready_for_input(false); to_be_scanned.insert(ip);}voidTCP_Client::dequeue_from_scan(Interest *ip){ to_be_scanned.remove(ip); if (!to_be_scanned.size()) conn.ready_for_input(true);}voidTCP_Client::enqueue_scanner(Scanner *sp){ assert(!my_scanner); my_scanner = sp; conn.ready_for_input(false);}voidTCP_Client::post_event(const Event& event, Request request, const char *path){ conn.send_event(event, request, path); Log::debug("sent event to %s: request %d \"%s\" %s", name(), request, path, event.name());}//////////////////////////////////////////////////////////////////////////////// Random kludges// If we're not running in insecure_compatibility mode,// and we haven't already logged a message for this connection,// put a message in the syslog saying "this client was denied...// try insecure_compat mode."voidTCP_Client::suggest_insecure_compat(const char *path){ if (insecure_compat_suggested) return; // if cred isn't valid it could be a remote connection. if ((cred.is_valid()) && (!Cred::insecure_compat_enabled()) && (cred.uid() == Cred::get_untrusted_uid())) { // XXX add a config option to turn off this message! Log::log(Log::INFO, "Client \"%s\", whose requests are being serviced " "as uid %d, was denied access on %s. If this client might " "be running as uid %d because it failed authentication, you " "might disable authentication. See the fam(1M) man page.", name(), cred.uid(), path, cred.uid()); insecure_compat_suggested = true; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -