[BACK]Return to TCP_Client.c++ CVS log [TXT][DIR] Up to [Development] / fam / fam

Annotation of fam/fam/TCP_Client.c++, Revision 1.1

1.1     ! trev        1: //  Copyright (C) 1999 Silicon Graphics, Inc.  All Rights Reserved.
        !             2: //
        !             3: //  This program is free software; you can redistribute it and/or modify it
        !             4: //  under the terms of version 2 of the GNU General Public License as
        !             5: //  published by the Free Software Foundation.
        !             6: //
        !             7: //  This program is distributed in the hope that it would be useful, but
        !             8: //  WITHOUT ANY WARRANTY; without even the implied warranty of
        !             9: //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  Further, any
        !            10: //  license provided herein, whether implied or otherwise, is limited to
        !            11: //  this program in accordance with the express provisions of the GNU
        !            12: //  General Public License.  Patent licenses, if any, provided herein do not
        !            13: //  apply to combinations of this program with other product or programs, or
        !            14: //  any other product whatsoever.  This program is distributed without any
        !            15: //  warranty that the program is delivered free of the rightful claim of any
        !            16: //  third person by way of infringement or the like.  See the GNU General
        !            17: //  Public License for more details.
        !            18: //
        !            19: //  You should have received a copy of the GNU General Public License along
        !            20: //  with this program; if not, write the Free Software Foundation, Inc., 59
        !            21: //  Temple Place - Suite 330, Boston MA 02111-1307, USA.
        !            22:
        !            23: #include "TCP_Client.h"
        !            24:
        !            25: #include <assert.h>
        !            26: #include <ctype.h>
        !            27: #include <stdio.h>
        !            28: #include <stdlib.h>
        !            29: #include <string.h>
        !            30: #include <unistd.h>
        !            31:
        !            32: #include "Cred.h"
        !            33: #include "Event.h"
        !            34: #include "Interest.h"
        !            35: #include "Log.h"
        !            36: #include "Scanner.h"
        !            37: #include "Listener.h"
        !            38:
        !            39: //////////////////////////////////////////////////////////////////////////////
        !            40: //  Construction/destruction
        !            41:
        !            42: TCP_Client::TCP_Client(in_addr host, int fd, Cred &cr)
        !            43:     : MxClient(host), cred(cr), my_scanner(NULL),
        !            44:       conn(fd, input_handler, unblock_handler, this),
        !            45:       insecure_compat_suggested(false)
        !            46: {
        !            47:     assert(fd >= 0);
        !            48:
        !            49:     // Set client's name.
        !            50:
        !            51:     char namebuf[20];
        !            52:     sprintf(namebuf, "client %d", fd);
        !            53:     name(namebuf);
        !            54:     Log::debug("new connection from %s", name());
        !            55: }
        !            56:
        !            57: TCP_Client::~TCP_Client()
        !            58: {
        !            59: }
        !            60:
        !            61: //////////////////////////////////////////////////////////////////////////////
        !            62: //  Input
        !            63:
        !            64: bool
        !            65: TCP_Client::input_handler(const char *msg, unsigned nbytes, void *closure)
        !            66: {
        !            67:     TCP_Client *client = (TCP_Client *) closure;
        !            68:     if (msg) {
        !            69: 	return client->input_msg(msg, nbytes);
        !            70:     }
        !            71:     else
        !            72:     {   Log::debug("lost connection from %s", client->name());
        !            73: 	delete client;			// NULL msg means connection closed.
        !            74:         return true;
        !            75:     }
        !            76: }
        !            77:
        !            78: bool
        !            79: TCP_Client::input_msg(const char *msg, int size)
        !            80: {
        !            81:     // Parse the first message.
        !            82:     // The first message is:
        !            83:     //	    opcode, request number, uid, gid, file name.
        !            84:
        !            85:     // If there's an error parsing the message, return false to cause
        !            86:     // the connection to the misbehaving client to be closed.
        !            87:
        !            88:     bool got_N_with_groups = false;
        !            89:     char *p = (char *) msg, *q;
        !            90:     char opcode = *p++;
        !            91:     Request reqnum = strtol(p, &q, 10);
        !            92:     if (p == q)
        !            93:     {
        !            94: 	Log::error("bad message (no request id)");
        !            95:         return false;
        !            96:     }
        !            97:     p = q;
        !            98:     uid_t uid = strtol(p, &q, 10);
        !            99:     if (p == q)
        !           100:     {
        !           101: 	Log::error("bad message (no uid)");
        !           102:         return false;
        !           103:     }
        !           104:     p = q;
        !           105:     gid_t gid = strtol(p, &q, 10);
        !           106:     if (p == q)
        !           107:     {
        !           108: 	Log::error("bad message (no gid)");
        !           109:         return false;
        !           110:     }
        !           111:     p = q;
        !           112:
        !           113:     // Advance past the space
        !           114:     p++;
        !           115:
        !           116:     char filename[PATH_MAX + 1];
        !           117:     int i;
        !           118:     for (i = 0; *p; i++)
        !           119:     {   if (i >= PATH_MAX)
        !           120: 	{   Log::error("%s path name too long (%d chars)", name(), i);
        !           121: 	    return false;
        !           122: 	}
        !           123: 	filename[i] = *p++;
        !           124:     }
        !           125:     filename[i] = '\0';
        !           126:     if (i > 0 && filename[i - 1] == '\n')
        !           127: 	filename[i - 1] = '\0';		// strip the trailing newline
        !           128:     p++;
        !           129:
        !           130:     // Parse the second message, if any.
        !           131:     // The second message is:
        !           132:     //	    ngroups, group, group, group ...
        !           133:
        !           134:     //  This is a raging kludge.  got_N_with_groups is how a client says
        !           135:     //  "create a UNIX domain socket for local communication with me."
        !           136:     //  You guessed that, right?
        !           137:     //
        !           138:     //  The reason it's done this way, rather than by having fam listen
        !           139:     //  for connections from local clients on a unix domain socket in addition
        !           140:     //  to the internet domain socket, is that when fam is started by inetd,
        !           141:     //  the first client would have to connect on the internet domain socket
        !           142:     //  anyway to make inetd exec fam.
        !           143:     if ((opcode == 'N') && (p < msg + size - 1)) got_N_with_groups = true;
        !           144:
        !           145:     //  If no Cred is set on the connection, that means we trust the uid
        !           146:     //  & gids supplied in the message.
        !           147:     Cred msg_cred = cred;
        !           148:     if (!msg_cred.is_valid())
        !           149:     {
        !           150:         gid_t *grouplist = &gid;
        !           151:         int ngroups = 1;
        !           152:         if (p < msg + size - 1)
        !           153:         {
        !           154:             ngroups += strtol(p, &p, 10);
        !           155:             int maxgroups = sysconf(_SC_NGROUPS_MAX);
        !           156:
        !           157:             if (ngroups > maxgroups)
        !           158:             {
        !           159:                 Log::info("message contained %i groups, but group list was"
        !           160:                           " truncated to %i because of _SC_NGROUPS_MAX",
        !           161:                           ngroups, maxgroups);
        !           162:                 ngroups = maxgroups;
        !           163:             }
        !           164:             grouplist = new gid_t[ngroups];
        !           165:             grouplist[0] = gid;
        !           166: 	    for (int i = 1; i < ngroups; i++)
        !           167: 	    {
        !           168: 	        grouplist[i] = strtol(p, &q, 10);
        !           169: 	        if (p == q)
        !           170:                 {
        !           171: 	            Log::error("bad message (%d additional groups expected, %d found)", ngroups - 1, i);
        !           172:                     ngroups = i;
        !           173:                     break;
        !           174:                 }
        !           175: 	        p = q;
        !           176: 	    }
        !           177:         }
        !           178:
        !           179:         Cred c(uid, ngroups, grouplist, conn.get_fd());
        !           180:         msg_cred = c;
        !           181:         if (grouplist != &gid) delete [] grouplist;
        !           182:     }
        !           183:
        !           184:     // Process the message.
        !           185:
        !           186:     switch (opcode)
        !           187:     {
        !           188:     case 'W':				// Monitor File
        !           189:     {
        !           190: 	Log::debug("%s said: request %d monitor file \"%s\"",
        !           191: 		   name(), reqnum, filename);
        !           192: 	monitor_file(reqnum, filename, msg_cred);
        !           193: 	break;
        !           194:     }
        !           195:     case 'M':				// Monitor Directory
        !           196: 	Log::debug("%s said: request %d monitor dir \"%s\"",
        !           197: 		   name(), reqnum, filename);
        !           198: 	monitor_dir(reqnum, filename, msg_cred);
        !           199: 	break;
        !           200:
        !           201:     case 'C':				// Cancel
        !           202: 	Log::debug("%s said: cancel request %d", name(), reqnum);
        !           203: 	cancel(reqnum);
        !           204: 	break;
        !           205:
        !           206:     case 'S':				// Suspend
        !           207: 	Log::debug("%s said: suspend request %d", name(), reqnum);
        !           208: 	MxClient::suspend(reqnum);
        !           209: 	break;
        !           210:
        !           211:     case 'U':				// Resume
        !           212: 	Log::debug("%s said: resume request %d", name(), reqnum);
        !           213: 	MxClient::resume(reqnum);
        !           214: 	break;
        !           215:
        !           216:     case 'N':				// Client Name
        !           217: 	Log::debug("%s said: %s is %s, and %s a unix domain socket",
        !           218:                    name(), name(), filename,
        !           219:                    (got_N_with_groups ? "wants" : "doesn't want"));
        !           220: 	if (*filename && strcmp(filename, "test"))
        !           221: 	    name(filename);		// set this client's name
        !           222:
        !           223:         //  Check to see whether this client wants to switch to a unix
        !           224:         //  domain socket.  Create it owned by the uid the client says
        !           225:         //  they're running as.
        !           226:         if (got_N_with_groups) Listener::create_local_client(*this, uid);
        !           227:
        !           228: 	break;
        !           229:
        !           230:     //
        !           231:     //  Ignore these obsolete messages.
        !           232:     //
        !           233:     case 'D':
        !           234:     case 'V':
        !           235:     case 'E':
        !           236: 	break;
        !           237:
        !           238:     default:
        !           239: 	Log::error("%s said unknown request '%c' ('\\%3o')",
        !           240: 		   name(), opcode, opcode & 0377);
        !           241:         return false;
        !           242:     }
        !           243:
        !           244:     return true;
        !           245: }
        !           246:
        !           247: //////////////////////////////////////////////////////////////////////////////
        !           248: //  Output
        !           249:
        !           250: void
        !           251: TCP_Client::unblock_handler(void *closure)
        !           252: {
        !           253:     TCP_Client *client = (TCP_Client *) closure;
        !           254:
        !           255:     //  Continue scanner, if any.
        !           256:
        !           257:     Scanner *scanner = client->my_scanner;
        !           258:     if (scanner)
        !           259:     {	if (scanner->done())
        !           260: 	    client->my_scanner = NULL;
        !           261: 	else
        !           262: 	    return;
        !           263:     }
        !           264:
        !           265:     //  After scanner has run, scan more interests.
        !           266:
        !           267:     Interest *ip;
        !           268:     while (client->ready_for_events() && (ip = client->to_be_scanned.first()))
        !           269:     {   client->to_be_scanned.remove(ip);
        !           270: 	ip->scan();
        !           271:     }
        !           272:
        !           273:     //  Enable input if all enqueued work is done.
        !           274:
        !           275:     if (client->ready_for_events())
        !           276: 	client->conn.ready_for_input(true);
        !           277: }
        !           278:
        !           279: bool
        !           280: TCP_Client::ready_for_events()
        !           281: {
        !           282:     return conn.ready_for_output();
        !           283: }
        !           284:
        !           285: void
        !           286: TCP_Client::enqueue_for_scan(Interest *ip)
        !           287: {
        !           288:     if (!to_be_scanned.size())
        !           289: 	conn.ready_for_input(false);
        !           290:     to_be_scanned.insert(ip);
        !           291: }
        !           292:
        !           293: void
        !           294: TCP_Client::dequeue_from_scan(Interest *ip)
        !           295: {
        !           296:     to_be_scanned.remove(ip);
        !           297:     if (!to_be_scanned.size())
        !           298: 	conn.ready_for_input(true);
        !           299: }
        !           300:
        !           301: void
        !           302: TCP_Client::enqueue_scanner(Scanner *sp)
        !           303: {
        !           304:     assert(!my_scanner);
        !           305:     my_scanner = sp;
        !           306:     conn.ready_for_input(false);
        !           307: }
        !           308:
        !           309: void
        !           310: TCP_Client::post_event(const Event& event, Request request, const char *path)
        !           311: {
        !           312:     conn.send_event(event, request, path);
        !           313:     Log::debug("sent event to %s: request %d \"%s\" %s",
        !           314: 	       name(), request, path, event.name());
        !           315: }
        !           316:
        !           317: //////////////////////////////////////////////////////////////////////////////
        !           318: //  Random kludges
        !           319:
        !           320: //  If we're not running in insecure_compatibility mode,
        !           321: //  and we haven't already logged a message for this connection,
        !           322: //  put a message in the syslog saying "this client was denied...
        !           323: //  try insecure_compat mode."
        !           324: void
        !           325: TCP_Client::suggest_insecure_compat(const char *path)
        !           326: {
        !           327:     if (insecure_compat_suggested) return;
        !           328:
        !           329:     //  if cred isn't valid it could be a remote connection.
        !           330:     if ((cred.is_valid()) &&
        !           331:         (!Cred::insecure_compat_enabled()) &&
        !           332:         (cred.uid() == Cred::get_untrusted_uid()))
        !           333:     {
        !           334:         //  XXX add a config option to turn off this message!
        !           335:         Log::log(Log::INFO, "Client \"%s\", whose requests are being serviced "
        !           336:                  "as uid %d, was denied access on %s.  If this client might "
        !           337:                  "be running as uid %d because it failed authentication, you "
        !           338:                  "might disable authentication.  See the fam(1M) man page.",
        !           339:                  name(), cred.uid(), path, cred.uid());
        !           340:         insecure_compat_suggested = true;
        !           341:     }
        !           342: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>