예제 #1
0
    def start(self, addr, retries=5):
        """Start the web server listening on addr in a new coroutine.

        Try up to retries time to bind to that address.
        Raises an exception if the bind fails."""

        server_s = self._make_socket()
        done = 0
        save_errno = 0
        self.addr = addr
        while not done:
            for x in xrange(retries):
                try:
                    was_eaddrinuse = 0
                    server_s.bind(addr)
                except OSError, why:
                    if why.errno not in (errno.EADDRNOTAVAIL,
                                         errno.EADDRINUSE):
                        raise
                    else:
                        save_errno = 0
                        if why.errno == errno.EADDRINUSE:
                            was_eaddrinuse = 1
                else:
                    done = 1
                    break
                coro.sleep_relative(1)  # ... and retry
            else:
                coro.print_stderr(
                    "cannot bind to %s:%d after 5 attempts, errno = %d\n" %
                    (addr[0], addr[1], save_errno))
                if was_eaddrinuse:
                    qlog.write('WEBUI.PORT_IN_USE', addr[0], str(addr[1]))
                coro.sleep_relative(15)
예제 #2
0
    def start (self, addr, retries=5):
        """Start the web server listening on addr in a new coroutine.

        Try up to retries time to bind to that address.
        Raises an exception if the bind fails."""

        server_s = self._make_socket()
        done = 0
        save_errno = 0
        self.addr = addr
        while not done:
            for x in xrange (retries):
                try:
                    was_eaddrinuse = 0
                    server_s.bind (addr)
                except OSError, why:
                    if why.errno not in (errno.EADDRNOTAVAIL, errno.EADDRINUSE):
                        raise
                    else:
                        save_errno = 0
                        if why.errno == errno.EADDRINUSE:
                            was_eaddrinuse = 1
                else:
                    done = 1
                    break
                coro.sleep_relative(1) # ... and retry
            else:
                coro.print_stderr ("cannot bind to %s:%d after 5 attempts, errno = %d\n" % (addr[0], addr[1], save_errno))
                if was_eaddrinuse:
                    qlog.write('WEBUI.PORT_IN_USE',
                               addr[0], str(addr[1]))
                coro.sleep_relative(15)
예제 #3
0
 def cmd_retr (self, line):
     if len(line) < 2:
         self.command_not_understood (string.join (line))
     else:
         file = line[1]
         if not self.filesystem.isfile (file):
             self.respond ('550 No such file')
         else:
             try:
                 # FIXME: for some reason, 'rt' isn't working on win95
                 mode = 'r'+self.type_mode_map[self.current_mode]
                 fd = self.open (file, mode)
             except (OSError, IOError), why:
                 self.respond ('553 could not open file for reading: %r' % why[0])
                 return
             try:
                 self.respond (
                     "150 Opening %s mode data connection for file '%s'" % (
                         self.type_map[self.current_mode],
                         file
                         )
                     )
                 conn, addr = self.make_data_channel()
                 if conn:
                     try:
                         fd.seek(0, 2)
                         filesize = fd.tell()
                         fd.seek(0, 0)
                         qlog.write('FTPD.DOWNLOAD', self.session_id, file, filesize)
                         if self.restart_position:
                             # try to position the file as requested, but
                             # give up silently on failure (the 'file object'
                             # may not support seek())
                             try:
                                 fd.seek (self.restart_position)
                             except:
                                 pass
                             self.restart_position = 0
                         try:
                             self.send_file (conn, fd)
                         except OSError, why:
                             self.respond ('451 Transfer aborted. %s' % (str(why)))
                         except coro.TimeoutError:
                             self.respond ('421 Transfer timed out.')
                         except coro.Shutdown:
                             self.respond ('451 Transfer aborted.  FTP service shutting down.')
                         else:
                             self.respond ('226 Transfer Complete')
                     finally:
                         conn.close()
예제 #4
0
 def run (self):
     try:
         try:
             self._run()
         except coro.Shutdown:
             # We've been asked to shutdown
             return
         except:
             qlog.write('COMMON.APP_FAILURE', tb.traceback_string() + `self`)
     finally:
         if self.user:
             qlog.write('FTPD.LOGOUT', self.session_id, self.user)
         self.close()
         self.server.session_done(self)
         # remove cycle
         del self.stream
예제 #5
0
 def _run (self):
     """Listens on the FTP port accepting connections and spawning sessions."""
     self.thread_id = coro.current().thread_id()
     s = coro.make_socket (socket.AF_INET, socket.SOCK_STREAM)
     try:
         s.set_reuse_addr()
         done = 0
         while not done:
             for x in xrange (5):
                 try:
                     was_eaddrinuse = 0
                     s.bind ((self.ip, self.port))
                 except OSError, why:
                     if why[0] == errno.EACCES:
                         coro.print_stderr(
                             'Access denied binding to %s:%i.  Are you running as root?\n' % (self.ip, self.port))
                         return
                     elif why[0] == errno.EADDRINUSE:
                         was_eaddrinuse = 1
                     elif why[0] != errno.EADDRNOTAVAIL:
                         raise
                 else:
                     done = 1
                     break
                 coro.sleep_relative (1)
             else:
                 coro.print_stderr ("cannot bind to %s:%d after 5 attempts\n" % (self.ip, self.port))
                 if was_eaddrinuse:
                     qlog.write('FTPD.PORT_IN_USE',
                                self.ip, str(self.port))
                 coro.sleep_relative (15)
         s.listen (1024)
         while 1:
             conn_list = s.accept_many()
             for conn, addr in conn_list:
                 qlog.write('FTPD.CONNECTION', self.session_id, addr[0], self.ip)
                 session = self.channel (self, conn, addr, self.session_id)
                 self.session_id += 1
                 thread = coro.spawn (session.run)
                 thread.set_name (
                     "%s_%d" % (
                         session.__class__.__name__,
                         thread.thread_id()
                     )
                 )
                 self.clients.append(session)
예제 #6
0
 def _run (self):
     """Listens on the FTP port accepting connections and spawning sessions."""
     self.thread_id = coro.current().thread_id()
     s = coro.make_socket (socket.AF_INET, socket.SOCK_STREAM)
     try:
         s.set_reuse_addr()
         done = 0
         while not done:
             for x in xrange (5):
                 try:
                     was_eaddrinuse = 0
                     s.bind ((self.ip, self.port))
                 except OSError, why:
                     if why[0] == errno.EACCES:
                         coro.print_stderr('Access denied binding to %s:%i.  Are you running as root?\n' % (self.ip, self.port))
                         return
                     elif why[0] == errno.EADDRINUSE:
                         was_eaddrinuse = 1
                     elif why[0] != errno.EADDRNOTAVAIL:
                         raise
                 else:
                     done = 1
                     break
                 coro.sleep_relative (1)
             else:
                 coro.print_stderr ("cannot bind to %s:%d after 5 attempts\n" % (self.ip, self.port))
                 if was_eaddrinuse:
                     qlog.write('FTPD.PORT_IN_USE',
                                self.ip, str(self.port))
                 coro.sleep_relative (15)
         s.listen (1024)
         while 1:
             conn_list = s.accept_many()
             for conn, addr in conn_list:
                 qlog.write('FTPD.CONNECTION', self.session_id, addr[0], self.ip)
                 session = self.channel (self, conn, addr, self.session_id)
                 self.session_id += 1
                 thread = coro.spawn (session.run)
                 thread.set_name (
                     "%s_%d" % (
                         session.__class__.__name__,
                         thread.thread_id()
                         )
                     )
                 self.clients.append(session)
예제 #7
0
 def done (self, with_body=1):
     if not self._sent_headers:
         self.send_headers()
     if with_body and self._chunking:
         # note: there's an invisible 'footer' between the pair of CRLF's.
         #  it can be used to send certain additional types of headers.
         self.send ('0\r\n\r\n')
     if self._close:
         self._client.close ()
     self._done = 1
     qlog.write('WEBUI.HTTP_REQUEST',
                self._client.peer[0],
                 self._user_id,
                 self._session_id,
                 self._reply_code,
                 self._request,
                 '' # XXX: User agent
                 )
예제 #8
0
    def cmd_pass (self, line):
        if len(line) < 2:
            pw = ''
        else:
            pw = line[1]
        try:
            result, message, fs = self.server.authorizer.authorize (self, self.user, pw)
        except SystemError:
            result = None
            message = 'User %s access denied' % self.user

        if result:
            self.respond ('230 %s' % message)
            self.filesystem = fs
            self.authorized = 1
            qlog.write('FTPD.LOGIN', self.session_id, self.user)
        else:
            qlog.write('FTPD.LOGIN_FAILED', self.session_id, self.user)
            self.respond ('530 %s' % message)
예제 #9
0
 def done(self, with_body=1):
     if not self._sent_headers:
         self.send_headers()
     if with_body and self._chunking:
         # note: there's an invisible 'footer' between the pair of CRLF's.
         #  it can be used to send certain additional types of headers.
         self.send('0\r\n\r\n')
     if self._close:
         self._client.close()
     self._done = 1
     qlog.write(
         'WEBUI.HTTP_REQUEST',
         self._client.peer[0],
         self._user_id,
         self._session_id,
         self._reply_code,
         self._request,
         ''  # XXX: User agent
     )
예제 #10
0
    def _run (self, server_s):
        secure = self.is_secure()

        self.thread_id = coro.current().thread_id()
        while not self.shutdown_flag:
            try:
               conn, addr = server_s.accept()
               client = http_client()
               coro.spawn (client.run, conn, addr, self, self._handlers)
            except coro.Shutdown:
                # server-shutdown
                break
            except:
                qlog.write('COMMON.APP_FAILURE',
                           ('%s accept handler error %s' %
                             (self.__class__.__name__, coro.compact_traceback())))
                coro.sleep_relative(0.25)
                continue

        server_s.close()
        return None
예제 #11
0
    def read_body(self):
        """Read the message body, if any, so that it's cleared from
        the input stream.  This avoids problems with keep-alives if
        the request handler doesn't read the body itself.

        This used to be  done in the __init__method, but that can
        lead to a fatal error in the Python interpreter (see bug 3367).

        The ultimate solution is to fix the way connections are handled
        to ensure that we don't reuse the connection if the body wasn't
        fully read by the request handler."""

        self._body = ''
        clen = self.get_request_header('Content-Length')
        if clen:
            try:
                clen = int(clen)
                self._body = coro.with_timeout(60, self._client.read, clen)
                if len(self._body) < clen:
                    qlog.write('WEBUI.CONN_ERROR',
                               'http', id(self),
                                'Truncated body (%d<%d) (req:%s)' % \
                                (len(self._body), clen, self._request))
                    self._error = 1  # didn't get the body we were promised
            except coro.TimeoutError:
                qlog.write('WEBUI.CONN_ERROR', 'http', id(self),
                           'Body read timeout (req:%s)' % self._request)
                self._error = 1
            except ValueError:
                qlog.write('WEBUI.CONN_ERROR',
                           'http', id(self),
                            'Invalid Content-Length (%s) (req:%s)' % \
                            (clen, self._request)
                            )
                self._error = 1
예제 #12
0
    def _run(self, server_s):
        secure = self.is_secure()

        self.thread_id = coro.current().thread_id()
        while not self.shutdown_flag:
            try:
                conn, addr = server_s.accept()
                client = http_client()
                coro.spawn(client.run, conn, addr, self, self._handlers)
            except coro.Shutdown:
                # server-shutdown
                break
            except:
                qlog.write(
                    'COMMON.APP_FAILURE',
                    ('%s accept handler error %s' %
                     (self.__class__.__name__, coro.compact_traceback())))
                coro.sleep_relative(0.25)
                continue

        server_s.close()
        return None
예제 #13
0
    def read_body(self):
        """Read the message body, if any, so that it's cleared from
        the input stream.  This avoids problems with keep-alives if
        the request handler doesn't read the body itself.

        This used to be  done in the __init__method, but that can
        lead to a fatal error in the Python interpreter (see bug 3367).

        The ultimate solution is to fix the way connections are handled
        to ensure that we don't reuse the connection if the body wasn't
        fully read by the request handler."""

        self._body = ''
        clen = self.get_request_header('Content-Length')
        if clen:
            try:
                clen = int(clen)
                self._body = coro.with_timeout(
                    60,
                    self._client.read,
                    clen)
                if len(self._body) < clen:
                    qlog.write('WEBUI.CONN_ERROR',
                               'http', id(self),
                                'Truncated body (%d<%d) (req:%s)' % \
                                (len(self._body), clen, self._request))
                    self._error = 1 # didn't get the body we were promised
            except coro.TimeoutError:
                qlog.write('WEBUI.CONN_ERROR',
                           'http', id(self),
                            'Body read timeout (req:%s)' % self._request)
                self._error = 1
            except ValueError:
                qlog.write('WEBUI.CONN_ERROR',
                           'http', id(self),
                            'Invalid Content-Length (%s) (req:%s)' % \
                            (clen, self._request)
                            )
                self._error = 1
예제 #14
0
    def run(self, conn, peer, server_obj, handlers):
        self.conn = conn
        self.server = server_obj
        self.peer = peer
        # Note that peer could be a fake address, and server_obj can be None.
        # These indicate a "backdoor" request from the gui.

        try:
            try:
                count = 0

                qlog.write('WEBUI.CONN_INIT', 'http', id(self), peer[0],
                           peer[1])

                while 1:
                    if self.server and self.server.shutdown_flag:
                        break
                    try:
                        # We use self.stream to read the header line-by-line
                        # and then switch to reading directly from the socket
                        # for the body (if needed).  Reuse the previous
                        # instance if it exists, to support HTTP pipelining.
                        if not self.stream:
                            self.stream = read_stream.stream_reader(
                                self.conn.recv)
                        request_line = self.read_line()
                        if not request_line:
                            break
                    except socket.error:
                        qlog.write('WEBUI.CONN_ERROR', 'http', id(self),
                                   'socket error')
                        break

                    count = count + 1
                    headers = self.read_header()
                    #print '\n'.join (headers) + '\n\n'
                    request = http_request(self, request_line, headers)
                    request.read_body()
                    if request._error:
                        # Bad Request
                        request.error(400)
                        return
                    else:
                        try:
                            try:
                                handler = self.pick_handler(handlers, request)

                                if handler:
                                    handler.handle_request(request)
                                else:
                                    self.not_found(request)

                                if not request._done:
                                    request.done()
                            except OSError, err:
                                if err[0] == errno.EPIPE:
                                    pass  # ignore broken pipe error
                                else:
                                    raise  # process exception in outer try
                        # These exceptions are used inside the coro
                        # stuff and shouldn't be thrown away
                        except (coro.TimeoutError, coro.Interrupted):
                            raise
                        except:
                            tb = coro.compact_traceback()
                            ## sys.stderr.write (repr(tb))
                            request.error(500, tb)
                            qlog.write('COMMON.APP_FAILURE',
                                       tb + ' request: ' + ` request `)
                            tb = None

                        if request._close:
                            # ok, this gets interesting.    the connection needs to close
                            # here. the finally clause below isn't getting hit because
                            # the session and client are running in the same coroutine.
                            # that's bad, I think.
                            conn.close()
                            break

                    # this should be a policy decision of the owner of logfp
                    # self.logfp.flush()
            except read_stream.BufferOverflow:
                # Indicates a request header that exceeded the line
                # buffer, which may indicate an attack on the server.
                # We just close the connection without a response.
                # TODO:lrosenstein - log this since it may be an attack?
                qlog.write('WEBUI.CONN_ERROR', 'http', id(self),
                           'line buffer limit exceeded')
                pass
            except sslip.Error, why:
                # Most likely a problem with SSL negotiation
                qlog.write('WEBUI.CONN_ERROR', 'https', id(self), why[1])
                pass
            except OSError, err:
                # We got some kind of I/O error that wasn't handled
                # elsewhere.  Since this seem to happen because the
                # client closed the connection, it is safe to ignore
                # the exception.
                qlog.write('WEBUI.CONN_ERROR', 'http', id(self),
                           'OS error %s' % str(err[1]))
                pass
예제 #15
0
 def respond (self, resp):
     qlog.write('FTPD.SEND', self.session_id, resp)
     self.send (resp + '\r\n')
예제 #16
0
    def _run (self):
        self.thread_id = coro.current().thread_id()
        try:
            # send the greeting
            self.send_greeting()

            while not self.shutdown_flag:
                line, eof = self.read_line_timeout()
                if eof:
                    break
                line = orig_line = line.lstrip()

                parts = line.split()
                if len (parts) < 1:
                    self.command_not_understood ('')
                    continue
                command = parts[0].lower()
                if len(parts) > 1:
                    args = ' '.join (parts[1:])
                    # If command arguments include null character, python path parsing
                    # function will complain. Remove the null characters.
                    line = [command, args.replace('\0', '')]
                else:
                    line = [command]

                # watch especially for 'urgent' abort commands.
                if command.find ('abor') != -1:
                    # strip off telnet sync chars and the like...
                    while command and command[0] not in string.letters:
                        command = command[1:]
                fun_name = 'cmd_%s' % command
                if command != 'pass':
                    qlog.write('FTPD.RECV', self.session_id, repr(orig_line)[1:-1])
                else:
                    qlog.write('FTPD.RECV', self.session_id, line[0]+' <password>')
                self.in_buffer = ''
                if not hasattr (self, fun_name):
                    self.command_not_understood (line[0])
                    continue
                fun = getattr (self, fun_name)
                if (not self.authorized) and (command not in ('user', 'pass', 'help', 'quit')):
                    self.respond ('530 Please log in with USER and PASS')
                elif (not self.check_command_authorization (self.user, command)):
                    self.command_not_authorized (command)
                else:
                    if hasattr (self, '_syntax_%s' % command):
                        r = getattr (self, '_syntax_%s' % command)
                        m = re.match (r, orig_line, re.IGNORECASE)
                        if m is None:
                            self.respond ('501 Syntax error in parameters or arguments')
                            continue
                    try:
                        result = apply (fun, (line,))
                    except OSError, why:
                        if why[0] in self.disconnect_errors:
                            # log it & ignore
                            qlog.write ('FTPD.DISCONNECT', self.session_id, why.strerror)
                            break
                        else:
                            raise
                    except coro.TimeoutError:
                        qlog.write('FTPD.DISCONNECT', self.session_id, 'Remote side timed out')
                        break
                    except coro.Interrupted:
                        raise
                    except:
                        self.server.total_exceptions.increment()
                        qlog.write('COMMON.APP_FAILURE', tb.traceback_string() +
                            ' fun: ' + `fun` + ' line: ' + `line`)
                        self.respond ('451 Server Error')
                        self.close_passive_acceptor()
                    else:
                        if result == 'quit':
                            break
예제 #17
0
                self.respond ('500 line too long.  good-bye')
            except coro.TimeoutError:
                pass
            except OSError:
                pass
        except coro.TimeoutError:
            try:
                self.respond ('421 timeout.  good-bye')
            except coro.TimeoutError:
                pass
            except OSError:
                pass
        except OSError, why:
            if why[0] in self.disconnect_errors:
                # log it & ignore
                qlog.write('FTPD.DISCONNECT', self.session_id, why.strerror)
            else:
                # Unknown error.  If it's something like EBADF, then there is
                # something seriously wrong.
                raise

    # the set of errors that indicate a connection problem
    disconnect_errors = (
        errno.ECONNRESET,
        errno.EHOSTUNREACH,
        errno.ECONNREFUSED,
        errno.EHOSTDOWN,
        errno.EPIPE,
        errno.ETIMEDOUT
        )
예제 #18
0
    def run (self, conn, peer, server_obj, handlers):
        self.conn = conn
        self.server = server_obj
        self.peer = peer
        # Note that peer could be a fake address, and server_obj can be None.
        # These indicate a "backdoor" request from the gui.

        try:
            try:
                count = 0

                qlog.write('WEBUI.CONN_INIT', 'http', id(self), peer[0], peer[1])

                while 1:
                    if self.server and self.server.shutdown_flag:
                        break
                    try:
                        # We use self.stream to read the header line-by-line
                        # and then switch to reading directly from the socket
                        # for the body (if needed).  Reuse the previous
                        # instance if it exists, to support HTTP pipelining.
                        if not self.stream:
                           self.stream = read_stream.stream_reader(self.conn.recv)
                        request_line = self.read_line()
                        if not request_line:
                            break
                    except socket.error:
                        qlog.write('WEBUI.CONN_ERROR', 'http', id(self), 'socket error')
                        break

                    count = count + 1
                    headers = self.read_header()
                    #print '\n'.join (headers) + '\n\n'
                    request = http_request (self, request_line, headers)
                    request.read_body()
                    if request._error:
                        # Bad Request
                        request.error (400)
                        return
                    else:
                        try:
                            try:
                                handler = self.pick_handler (handlers, request)

                                if handler:
                                    handler.handle_request (request)
                                else:
                                    self.not_found (request)

                                if not request._done:
                                    request.done()
                            except OSError, err:
                                if err[0] == errno.EPIPE:
                                    pass # ignore broken pipe error
                                else:
                                    raise # process exception in outer try
                        # These exceptions are used inside the coro
                        # stuff and shouldn't be thrown away
                        except (coro.TimeoutError, coro.Interrupted):
                            raise
                        except:
                            tb = coro.compact_traceback()
                            ## sys.stderr.write (repr(tb))
                            request.error (500, tb)
                            qlog.write('COMMON.APP_FAILURE',
                                       tb + ' request: ' + `request`)
                            tb = None

                        if request._close:
                            # ok, this gets interesting.    the connection needs to close
                            # here. the finally clause below isn't getting hit because
                            # the session and client are running in the same coroutine.
                            # that's bad, I think.
                            conn.close()
                            break

                    # this should be a policy decision of the owner of logfp
                    # self.logfp.flush()
            except read_stream.BufferOverflow:
                # Indicates a request header that exceeded the line
                # buffer, which may indicate an attack on the server.
                # We just close the connection without a response.
                # TODO:lrosenstein - log this since it may be an attack?
                qlog.write('WEBUI.CONN_ERROR', 'http',
                                                 id(self), 'line buffer limit exceeded')
                pass
            except sslip.Error, why:
                # Most likely a problem with SSL negotiation
                qlog.write('WEBUI.CONN_ERROR',
                           'https',
                            id(self),
                            why[1])
                pass
            except OSError, err:
                # We got some kind of I/O error that wasn't handled
                # elsewhere.  Since this seem to happen because the
                # client closed the connection, it is safe to ignore
                # the exception.
                qlog.write('WEBUI.CONN_ERROR',
                    'http', id(self), 'OS error %s' % str(err[1]))
                pass