Exemplo n.º 1
0
    def gen_nak(self, req, nak=gdp_pb2.NAK_C_BADREQ):
        resp = dict()
        resp['src'] = req['dst']
        resp['dst'] = req['src']

        resp_payload = gdp_pb2.GdpMessage()
        resp_payload.ParseFromString(req['data'])
        resp_payload.cmd = nak
        resp_payload.nak.ep_stat = 0

        resp['data'] = resp_payload.SerializeToString()

        return resp
Exemplo n.º 2
0
    def gen_nak(self, req, nak=gdp_pb2.NAK_C_BADREQ):

        logging.info("sending a NAK(%d) [src:%r, dst:%r]" %
                     (nak, self.printable_name(
                         req['dst']), self.printable_name(req['src'])))
        resp = dict()
        resp['src'] = req['dst']
        resp['dst'] = req['src']

        resp_payload = gdp_pb2.GdpMessage()
        resp_payload.ParseFromString(req['data'])
        resp_payload.cmd = nak
        resp_payload.nak.ep_stat = 0

        resp['data'] = resp_payload.SerializeToString()
        return resp
Exemplo n.º 3
0
    def request_handler(self, req):
        """
        The routine that gets invoked when a PDU is received. In our case,
        it can be either a new request (from a client), or a response from
        a log-server.
        """

        # first, deserialize the payload from req pdu.
        payload = gdp_pb2.GdpMessage()
        payload.ParseFromString(req['data'])

        # early exit if a router told us something (usually not a good
        # sign)
        if payload.cmd >= GDP_NAK_R_MIN and \
                        payload.cmd <= GDP_NAK_R_MAX:
            logger.warning("Routing error, src: %r", req['src'])
            return

        # check if it's a request from a client or a response from a logd.
        if payload.cmd < GDP_ACK_MIN:  ## it's a command

            ## First check for any error conditions. If any of the
            ## following occur, we ought to send back a NAK
            if req['src'] in self.logservers:
                logging.warning("error: received cmd %d from server",
                                payload.cmd)
                return self.gen_nak(req, gdp_pb2.NAK_C_BADREQ)

            if payload.cmd != gdp_pb2.CMD_CREATE:
                logging.warning("error: recieved unknown request")
                return self.gen_nak(req, gdp_pb2.NAK_S_NOTIMPL)

            ## By now, we know the request is a CREATE request from a client

            ## figure out the data we need to insert in the database
            humanname, logname = self.extract_name(payload.cmd_create)

            if humanname is not None and self.namedb_conn is not None:
                ## Add this to the humanname=>logname mapping directory
                try:
                    # Note that logname is the internal 256-bit name
                    # (and not a printable name)
                    self.add_to_hongd(humanname, logname)
                except mariadb.Error as error:
                    logging.warning("Couldn't add mapping. %s", str(error))
                    ## XXX what is the correct behavior? exit?
                    return self.gen_nak(req, gdp_pb2.NAK_C_CONFLICT)

            srvname = random.choice(self.logservers)
            ## private information that we will need later
            creator = req['src']
            rid = payload.rid

            ## log this to our backend database. Generate printable
            ## names (except the null character, which causes problems)
            __logname = gdp.GDP_NAME(logname,
                                     force_internal=True).printable_name()[:-1]
            __srvname = gdp.GDP_NAME(srvname,
                                     force_internal=True).printable_name()[:-1]
            __creator = gdp.GDP_NAME(creator,
                                     force_internal=True).printable_name()[:-1]

            logging.info(
                "Received Create request for logname %r, "
                "picking server %r", __logname, __srvname)

            try:

                with self.lock:

                    logging.debug("inserting to database %r, %r, %r, %d",
                                  __logname, __srvname, __creator, rid)
                    self.cur.execute(
                        """INSERT INTO logs (logname, srvname,
                                    creator, rid) VALUES(?,?,?,?);""",
                        (__logname, __srvname, __creator, rid))
                    self.conn.commit()

                    spoofed_req = req.copy()
                    spoofed_req['src'] = req['dst']
                    spoofed_req['dst'] = srvname
                    ## make a copy of payload so that we can change rid
                    __spoofed_payload = gdp_pb2.GdpMessage()
                    __spoofed_payload.ParseFromString(req['data'])
                    __spoofed_payload.rid = self.cur.lastrowid
                    spoofed_req['data'] = __spoofed_payload.SerializeToString()

                    # now return this spoofed request back to transport layer
                    # Since we have overridden the destination, it will go
                    # to a log server instead of the actual client
                    return spoofed_req

            except sqlite3.IntegrityError:

                logging.warning("Log already exists")
                return self.gen_nak(req, gdp_pb2.NAK_C_CONFLICT)

            ## Send a spoofed request to the logserver

        else:  ## response.

            ## Sanity checking
            if req['src'] not in self.logservers:
                logging.warning(
                    "error: received a response from non-logserver")
                return self.gen_nak(req, gdp_pb2.NAK_C_BADREQ)

            logging.info("Response from log-server, row %d", payload.rid)

            with self.lock:

                ## Fetch the original creator and rid from our database
                self.cur.execute(
                    """SELECT creator, rid, ack_seen FROM logs
                                            WHERE rowid=?""", (payload.rid, ))
                dbrows = self.cur.fetchall()

            good_resp = len(dbrows) == 1
            if good_resp:
                (__creator, orig_rid, ack_seen) = dbrows[0]
                creator = gdp.GDP_NAME(__creator).internal_name()
                if ack_seen != 0:
                    good_resp = False

            if not good_resp:

                logging.warning("error: bogus response")
                return self.gen_nak(req, gdp_pb2.NAK_C_BADREQ)

            else:

                with self.lock:
                    logging.debug("Setting ack_seen to 1 for row %d",
                                  payload.rid)
                    self.cur.execute(
                        """UPDATE logs SET ack_seen=1
                                        WHERE rowid=?""", (payload.rid, ))
                    self.conn.commit()

            # create a spoofed reply and send it to the client
            spoofed_reply = req.copy()
            spoofed_reply['src'] = req['dst']
            spoofed_reply['dst'] = creator

            spoofed_payload = gdp_pb2.GdpMessage()
            spoofed_payload.ParseFromString(req['data'])
            spoofed_payload.rid = orig_rid

            spoofed_reply['data'] = spoofed_payload.SerializeToString()

            # return this back to the transport layer
            return spoofed_reply
Exemplo n.º 4
0
    def request_handler(self, req):
        """
        The routine that gets invoked when a PDU is received. In our case,
        it can be either a new request (from a client), or a response from
        a log-server.
        """

        # first, deserialize the payload from req pdu.
        payload = gdp_pb2.GdpMessage()
        payload.ParseFromString(req['data'])

        # early exit if a router told us something (usually not a good
        # sign)
        if payload.cmd >= GDP_NAK_R_MIN and \
                         payload.cmd <= GDP_NAK_R_MAX:
            logger.warning("Routing error, src: %r",
                           self.printable_name(req['src']))
            return

        # check if it's a request from a client or a response from a logd.
        if payload.cmd < GDP_ACK_MIN:  ## it's a command

            ## First check for any error conditions. If any of the
            ## following occur, we ought to send back a NAK
            if req['src'] in self.logservers:
                logging.warning("error: received cmd %d from server",
                                payload.cmd)
                return self.gen_nak(req, gdp_pb2.NAK_C_BADREQ)

            if payload.cmd != gdp_pb2.CMD_CREATE:
                logging.warning("error: recieved unknown request")
                return self.gen_nak(req, gdp_pb2.NAK_S_NOTIMPL)

            ## By now, we know the request is a CREATE request from a client

            ## figure out the data we need to insert in the database
            humanname, logname = self.extract_name(payload.cmd_create)

            ## Add this to the humanname=>logname mapping directory
            #FIXME this shouldn't be done before the log is successfully
            #FIXME created on the server!
            if humanname is not None and self.namedb_cpool is not None:
                try:
                    # Note that logname is the internal 256-bit name
                    # (and not a printable name)
                    self.add_to_hongd(humanname, logname)

                except mariadb.Error as error:
                    # XXX we should probably handle the situation where
                    # this is simply a retransmit from an aggressive client.
                    logging.warning("Couldn't add mapping. %s", str(error))
                    return self.gen_nak(req, gdp_pb2.NAK_C_CONFLICT)

                except PoolExhausted as error:
                    logging.warning("DB connection pool exhausted")
                    return self.gen_nak(req, gdp_pb2.NAK_S_INTERNAL)

                except Exception as e:
                    ## report back to the client that something went wrong
                    logging.warning("Generic exception: %r", e)
                    return self.gen_nak(req, gdp_pb2.NAK_S_INTERNAL)

            srvname = random.choice(self.logservers)
            ## private information that we will need later
            creator = req['src']
            rid = payload.rid

            ## log this to our backend database. Generate printable
            ## names (except the null character, which causes problems)
            __logname = self.printable_name(logname)
            __srvname = self.printable_name(srvname)
            __creator = self.printable_name(creator)

            logging.info(
                "Received Create request for logname %r, "
                "picking server %r", __logname, __srvname)

            try:
                ## Add this information to the dupdb, and get back the
                ## row ID in the database that we will include in the spoofed
                ## request to an actual log-server
                __rid = self.add_to_dupdb(__logname, __srvname, __creator, rid)

                spoofed_req = req.copy()
                spoofed_req['src'] = req['dst']
                spoofed_req['dst'] = srvname
                ## make a copy of payload so that we can change rid
                __spoofed_payload = gdp_pb2.GdpMessage()
                __spoofed_payload.ParseFromString(req['data'])
                __spoofed_payload.rid = __rid
                spoofed_req['data'] = __spoofed_payload.SerializeToString()

                # now return this spoofed request back to transport layer
                # Since we have overridden the destination, it will go
                # to a log server instead of the actual client
                return spoofed_req

            except sqlite3.IntegrityError:

                ## We reach here if we are trying to add a duplicate
                ## entry in our database (which is the whole purpose
                ## of such bookkeeping).
                ## We send a NAK to the creator.
                logging.warning("Log already exists")
                return self.gen_nak(req, gdp_pb2.NAK_C_CONFLICT)

            except Exception as e:

                ## Generic exception
                logging.warning("Got generic exception %r", e)
                return self.gen_nak(req, gdp_pb2.NAK_S_INTERNAL)

        else:  ## response.

            ## Sanity checking
            if req['src'] not in self.logservers:
                logging.warning(
                    "error: received a response from non-logserver")
                return self.gen_nak(req, gdp_pb2.NAK_C_BADREQ)

            logging.info("Response from log-server, row %d", payload.rid)

            try:
                (creator, orig_rid) = self.fetch_creator(payload.rid)
            except UnknownResponse as e:
                ## this happens because either we don't know about the
                ## specific request ID, or we have already received a
                ## response for the said request ID. In any case, it is
                ## appropriate to return a NAK back to the log-server
                logging.warning("error: bogus response")
                return self.gen_nak(req, gdp_pb2.NAK_C_BADREQ)
            except Exception as e:
                ## We shouldn't be returning any NAK back to a log-server.
                ## Ideally, we should be returning a NAK back to the original
                ## client, but we don't have a name for the client yet. The
                ## most appropriate strategy is to simply report the error on
                ## the console.
                logging.warning("Generic exception: %r", e)
                return None

            # create a spoofed reply and send it to the client
            spoofed_reply = req.copy()
            spoofed_reply['src'] = req['dst']
            spoofed_reply['dst'] = creator

            spoofed_payload = gdp_pb2.GdpMessage()
            spoofed_payload.ParseFromString(req['data'])
            spoofed_payload.rid = orig_rid

            spoofed_reply['data'] = spoofed_payload.SerializeToString()

            # return this back to the transport layer
            return spoofed_reply