FC2のFTPサーバから強制的に切断される

date:2013-04-21
status:close
type:bug

結論

FC2のFTPサーバから強制的に切断されたら 再度セッションを作り直してアップロードするように処理を変更しました。

内容

FTPサーバにFC2を使用しているのですがアップロードの途中で強制的に切断されます。 アップロードの際に必ずディレクトリを作成しているのですが、 それがDoS攻撃とされてバンされているのかも知れません。

試しにディレクトリの作成コマンドを投げないようにしてみたところ切断されなくなりました。 しかしこれでは新しいディレクトリを作成した時に作成できません。

FTPクライアントには ftputil を使用しています。 ディレクトリが既にあるかどうかを確認できるとベストです。

ftputil.FTPHost.path にはディレクトリがあるかを調べるための関数があります。 使い方は ftputil にまとめました。

ディレクトリの作成はディレクトリが既にあるかを調べてから作成するように処理を変更しました。

しかしセッションが強制的に切断される現象はかわりませんでした。 しかたがないので、切断されても再度セッションを作り直して アップロードするように処理を変更しました。

現在のアップロード用のスクリプトはこのようになっています。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#! /usr/bin/env python
#-*- coding: utf-8 -*-
import sys
import os
cur = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(cur, '../_etc_'))

import re
import time
import socket

import ftputil

import ftp_conf

exts = [
    'html',
    'htm',
    'hdm',
    'wml',
    'gif',
    'png',
    'jpg',
    'jpeg',
    'css',
    'swf',
    'ico',
    'js',
    'class',
    'mel',
    'jar',
    'au',
    'dor',
    'txt',
    'htaccess',
    'htpasswd',
    'dat',
    'rss',
    'ref',
    'xml',
    'pmd',
    'ppt',
    'pptx',
    'pdf',
    'doc',
    'docx',
    'gra',
    'mny',
    'mdb',
    'pwi',
    'xls',
    'xlsx',
    'mid',
    ]

def main():
    host = ftp_conf.host
    user = ftp_conf.user
    pswd = ftp_conf.pswd
    root = ftp_conf.local
    
    print host
    print user
    print pswd
    print root

    regext = re.compile('\.({0})$'.format('|'.join(exts)), re.I)
    regpic = re.compile('\.(png|bmp|jpg)$', re.I)

    cache_name = '_upload_cache_'
    upload_cache = []
    if os.path.isfile(cache_name):
        with open(cache_name, 'rb') as fp:
            upload_cache = fp.readlines()
            
    os.chdir(root)
    cur = os.path.relpath(os.getcwd())
    filepaths = []
    for root, dirs, files in os.walk(cur):
        for filename in files:
            if regext.search(filename) and not regpic.search(filename):
                pp = os.path.join(root, filename)
                filepaths.append(pp)

    upload_cache = map(os.path.abspath, upload_cache)
    cache_file = open(cache_name, 'a+b')
    while filepaths:
        try:
            with ftputil.FTPHost(host, user, pswd) as host:
                while filepaths:
                    path = filepaths.pop()
                    path = path.replace('\\', '/')
                    dst = path.replace('./', '/', 1)
                    print path, '-->',
                    
                    if os.path.abspath(path) in upload_cache:
                        print 'skip'
                        continue
                
                    dirpath = os.path.dirname(dst)
                    if dirpath and not host.path.exists(dirpath):
                        try:
                            host.makedirs(dirpath)
                        except ftputil.ftp_error.PermanentError as err:
                            print err
                        except ftputil.ftp_error.FTPOSError as err:
                            print err

                    try:
                        if host.upload_if_newer(path, dst) is not False:
                            print 'ok'
                            cache_file.write('{0}\n'.format(path))
                        else:
                            print 'do not uploaded'
                    except ftputil.ftp_error.FTPIOError as err:
                        print err
                    except ftputil.ftp_error.FTPOSError as err:
                        print err
                        print dirpath
        except socket.error as err:
            print err
            time.sleep(5)

if __name__ == '__main__':
    main()

再発したのでスクリプトを変更

上記スクリプトを使用しアップロードを行っていましたが また上手くあがらなくなってしまいました。(ログ紛失)

ftputilをもう少し調べるとsyncするためのクラス ftputil.ftp_sync.Syncer
があることを発見しました。ドキュメントはちゃんと読まないとダメですね。

そのまま使ってみましたが無視するファイルは設定できないし、 強制的に切断されたときのリトライができないなどの課題がありました。 そこで ftputil.ftp_sync.Syncer を継承して 無視設定、リトライ処理を追加したクラスを作成しました。

現在は次のスクリプトを使用しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#! /usr/bin/env python
#-*- coding: utf-8 -*-
import sys
sys.path.append('../etc')
import os
import re
import time
import socket

import ftputil
import ftputil.ftp_sync

import ftp_conf

class Uploader(ftputil.ftp_sync.Syncer):
    def __init__(self, remote_conf, *args, **kwds):
        local = ftputil.ftp_sync.LocalHost()
        self.conf = remote_conf
        remote = self.get_target_host()
        super(Uploader, self).__init__(local, remote)

    def get_target_host(self):
        host = self.conf.host
        user = self.conf.user
        pswd = self.conf.pswd
        return ftputil.FTPHost(host, user, pswd)

    @property
    def target_pattern(self):
        if not hasattr(self, '_target_pattern'):
            exts = [
                'html', 'htm', 'hdm', 'wml', 'gif', 'png', 'jpg', 'jpeg',
                'css', 'swf', 'ico', 'js', 'class', 'mel', 'jar', 'au',
                'dor', 'txt', 'htaccess', 'htpasswd', 'dat', 'rss', 'ref',
                'xml', 'pmd', 'ppt', 'pptx', 'pdf', 'doc', 'docx', 'gra',
                'mny', 'mdb', 'pwi', 'xls', 'xlsx', 'mid', 'htaccess',
                ]
            self._target_pattern = re.compile(
                '\.({0})$'.format('|'.join(exts)), re.I)
        return self._target_pattern

    def is_ignore(self, path):
        return not self.target_pattern.search(path)

    def _sync_file(self, source_file, target_file):
        print target_file, '--->',
        if not self.is_ignore(source_file):
            err = None
            for ii in range(3):
                try:
                    rc = self._target.upload_if_newer(source_file,
                                                  target_file, mode='b')
                    if rc is True:
                        print 'upload'
                    elif rc is False:
                        print 'pass'
                    else:
                        print '???'
                    break
                except BaseException as err:
                    print 'error'
                    print type(err)
                    print err
                    print 'retry!!'
                    time.sleep(30)
                    self._target = self.get_target_host()
                    continue
            else:
                if err:
                    raise err
                    
        else:
            print 'ignore'

    def upload(self):
        self.sync(self.conf.local, '')        

def main():
    syncer = Uploader(ftp_conf)
    syncer.upload()

if __name__ == '__main__':
    main()

それに応じて make も修正しました。

目次

前のトピックへ

ftputilで画像が正しくアップロードできない

次のトピックへ

Google Analyticsを埋め込みたい

このページ

inserted by FC2 system