├── .gitignore ├── README.md ├── core ├── __init__.py ├── admin.py ├── apps.py ├── default.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── zmapd.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_job_status_path.py │ ├── 0003_auto_20160706_1102.py │ ├── 0004_auto_20160706_1703.py │ ├── 0005_job_pid.py │ ├── 0006_auto_20160706_1710.py │ ├── 0007_auto_20160706_1754.py │ ├── 0008_auto_20160706_2354.py │ ├── 0009_delete_command.py │ ├── 0010_auto_20160707_0927.py │ ├── 0011_command.py │ ├── 0012_auto_20160707_1655.py │ ├── 0013_auto_20160712_2235.py │ ├── 0014_auto_20160712_2307.py │ ├── 0015_auto_20160712_2307.py │ ├── 0016_auto_20160712_2315.py │ ├── 0017_auto_20160712_2340.py │ ├── 0018_auto_20160713_0018.py │ ├── 0019_job_subnets.py │ ├── 0020_auto_20160825_0944.py │ └── __init__.py ├── models.py ├── tests.py ├── urls.py ├── views.py └── zmapd.py ├── manage.py ├── requirements.txt ├── static ├── img │ ├── icon.png │ └── webzmap.png └── js │ ├── app.js │ └── template.js ├── templates ├── admin │ └── base_site.html ├── base.html ├── index.html ├── login.html └── rest_framework │ ├── base.html │ └── login.html ├── tools ├── __init__.py └── zmap.py └── webzmap ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *.bak 4 | *.log 5 | .idea/ 6 | *.iml 7 | *.sqlite3 8 | *.pyc 9 | ._* 10 | nohup.out 11 | static/upload 12 | workspace 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![WebZmap](http://www.webzmap.com/images/webzmap.png "WebZmap") 2 | 3 | **WebZmap** 是一个通过web方式管理运行zmap扫描任务, 并提供 **RESTful API** 方便第三方程序调用控制zmap任务 4 | 5 | 目前该项目还在开发中,很多功能待完善, 由于个人时间精力有限, 开发进度有点慢, 有兴趣的朋友可以fork, 并给我PR 6 | 7 | # Goal 8 | 9 | 1. 通过Web管理zmap任务(70%) 10 | 2. 通过RESTful API为第三方程序提供接口(60%) 11 | 3. 分布式扫描(0%) 12 | 13 | # Dependency 14 | 15 | 1. zmap 16 | 2. Python 2.7 17 | 3. django 1.10 18 | 4. requests 2.9.1 19 | 5. djangorestframework 3.4.4 20 | 21 | # Install 22 | 23 | 1. 安装 *zmap*, 安装方式请查看[zmap官方文档](https://zmap.io/download.html) 24 | 25 | 2. 克隆项目 26 | 27 | ```shell 28 | git clone https://github.com/fengyouchao/webzmap 29 | ``` 30 | 31 | 3. 安装依赖 32 | 33 | ```shell 34 | cd webzmap 35 | sudo -H pip install -r requirements.txt 36 | ``` 37 | 38 | 4. 初始化 39 | 40 | ```shell 41 | python manage.py migrate #创建数据库 42 | python manage.py createsuperuser #创建系统用户 43 | sudo python manage.py zmapd start #启动用于执行zmap任务的zmapd服务,必须以root权限执行 44 | ``` 45 | 46 | 5. 确认zmap执行路径 47 | ```shell 48 | where zmap 49 | ``` 50 | **WebZmap** 默认的zmap执行路径为 `/usr/local/sbin/zmap` 如果 `where zmap` 的路径不是该值, 编辑 `webzmap/settings.py`, 在文件最后添加以下内容: 51 | ```python 52 | ZMAP_PATH = 'your zmap bin path' 53 | ``` 54 | 55 | 6. 运行 56 | 57 | ```shell 58 | python manage.py runserver #请勿在生产环境使用此方式运行 59 | ``` 60 | 61 | 访问 `http://localhost:8000` 62 | 63 | # Deploy 64 | 65 | 如果要在生产环境中部署webzmap, 可以通过以下步骤完成: 66 | 67 | 1. 编辑`webzmap/settings.py`在文件最后添加以下内容 68 | 69 | ```python 70 | DEBUG = False 71 | ALLOWED_HOSTS = ['*'] # 设置允许访问该站点的host, * 表示任意host, 如果您希望只能通过域名访问,在这里设置域名 72 | ``` 73 | 74 | 2. 查看[Deploying Django](https://docs.djangoproject.com/en/1.10/howto/deployment/) 75 | 76 | # FAQ 77 | 78 | 1. 提交任务后任务一直处于Pending状态 79 | 80 | 请检查是否启动了zmapd服务 81 | 82 | ```shell 83 | sudo python manage.py zmapd status 84 | ``` 85 | 86 | 启动zmapd 87 | ```shell 88 | sudo python manage.py zmapd start 89 | ``` 90 | 91 | 2. 执行日志显示`[FATAL] csv: could not open output file (xxx/webzmap/workspace/xxx/status.txt)` 92 | 93 | 目前测试发现,这应该是zmap的一个bug,测试版本号为`2.1.1`, 在指定`-u`参数时,有一定概率发生此问题,目前解决办法为重新创建任务。 94 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/core/__init__.py -------------------------------------------------------------------------------- /core/admin.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.template import Template, Context 3 | from django.contrib import admin 4 | from .models import Job, WhiteListFile, BlackListFile, Command 5 | 6 | 7 | # Register your models here. 8 | class JobAdmin(admin.ModelAdmin): 9 | search_fields = ['name', 'id'] 10 | list_display = ['id', 'name', 'port','subnets', 'status', 'creation_time', 'output_path', 11 | 'log_path', 'status_path', 'pid', 'read_time', 'time_elapsed', 'time_remaining', 'percent_complete', 12 | 'active_send_threads', 'sent_total', 'sent_last_one_sec', 'sent_avg_per_sec', 'recv_success_total', 13 | 'recv_success_last_one_sec', 'recv_success_avg_per_sec', 'recv_total', 'recv_total_last_one_sec', 14 | 'recv_total_avg_per_sec', 'pcap_drop_total', 'drop_last_one_sec', 'drop_avg_per_sec', 15 | 'sendto_fail_total', 'sendto_fail_last_one_sec', 'sendto_fail_avg_per_sec', ] 16 | list_filter = ['status', 'creation_time'] 17 | readonly_fields = ['id', 'creation_time', 'output_path', 'log_path', 'start_time', 'end_time'] 18 | fieldsets = [ 19 | (None, {'fields': ['name', 'port', 'white_list_file', 'black_list_file']}), 20 | ('选项', {'fields': ['bandwidth', 'verbosity', 'status']}), 21 | ] 22 | 23 | ordering = ['-creation_time'] 24 | 25 | 26 | class WhiteListFileAdmin(admin.ModelAdmin): 27 | list_display = ['name', 'file', 'size', 'remark'] 28 | fieldsets = [ 29 | (None, {'fields': ['name', 'file', 'remark']}), 30 | ] 31 | 32 | 33 | class BlackListFileAdmin(admin.ModelAdmin): 34 | list_display = ['name', 'file', 'size', 'remark'] 35 | fieldsets = [ 36 | (None, {'fields': ['name', 'file', 'remark']}), 37 | ] 38 | 39 | 40 | class CommandAdmin(admin.ModelAdmin): 41 | list_display = ['creation_time', 'cmd', 'job', 'status'] 42 | fieldsets = [ 43 | (None, {'fields': ['job', 'cmd']}), 44 | ] 45 | 46 | 47 | admin.site.register(Job, JobAdmin) 48 | admin.site.register(WhiteListFile, WhiteListFileAdmin) 49 | admin.site.register(BlackListFile, BlackListFileAdmin) 50 | admin.site.register(Command, CommandAdmin) 51 | -------------------------------------------------------------------------------- /core/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class CoreConfig(AppConfig): 7 | name = 'core' 8 | -------------------------------------------------------------------------------- /core/default.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from webzmap.settings import WORK_DIR 3 | 4 | cwd = getattr(settings, "ZMAP_CWD", WORK_DIR) 5 | zmap_path = getattr(settings, "ZMAP_PATH", '/usr/local/sbin/zmap') 6 | max_bandwidth = getattr(settings, 'ZMAP_MAX_BANDWIDTH', 8) 7 | pid_file = getattr(settings, 'ZMAPD_PID_FILE', '/var/run/zmapd.pid') 8 | default_bandwidth = getattr(settings, 'ZMAP_DEFAULT_BANDWIDTH', 2) 9 | 10 | -------------------------------------------------------------------------------- /core/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/core/management/__init__.py -------------------------------------------------------------------------------- /core/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/core/management/commands/__init__.py -------------------------------------------------------------------------------- /core/management/commands/zmapd.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand, CommandError 2 | import os 3 | from core import zmapd 4 | import sys 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'zmapd - Zmap Manager Daemon' 9 | 10 | def add_arguments(self, parser): 11 | parser.add_argument('cmd', help="start or stop zmap manager", choices=["start", "stop", "restart", "status"]) 12 | 13 | def handle(self, *args, **options): 14 | if os.geteuid() != 0: 15 | raise CommandError("Only root can run this command") 16 | cmd = options['cmd'] 17 | if cmd == 'start': 18 | zmapd.start() 19 | elif cmd == 'stop': 20 | zmapd.stop() 21 | elif cmd == 'restart': 22 | zmapd.restart() 23 | else: 24 | zmapd.status() 25 | -------------------------------------------------------------------------------- /core/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-05 09:41 3 | from __future__ import unicode_literals 4 | 5 | import core.models 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='BlackListFile', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('file', models.FileField(upload_to=core.models.get_black_list_path, verbose_name='\u6587\u4ef6')), 24 | ('size', models.BigIntegerField(default=0, verbose_name='\u5927\u5c0f')), 25 | ('remark', models.CharField(blank=True, max_length=255, null=True, verbose_name='\u5907\u6ce8')), 26 | ], 27 | options={ 28 | 'verbose_name': '\u9ed1\u540d\u5355', 29 | 'verbose_name_plural': '\u9ed1\u540d\u5355', 30 | }, 31 | ), 32 | migrations.CreateModel( 33 | name='Job', 34 | fields=[ 35 | ('id', models.CharField(default=core.models.gen_job_id, max_length=32, primary_key=True, serialize=False, verbose_name='ID')), 36 | ('name', models.CharField(default='\u626b\u63cf\u4efb\u52a1', max_length=30, verbose_name='\u4efb\u52a1\u540d\u79f0')), 37 | ('port', models.IntegerField(verbose_name='\u626b\u63cf\u7aef\u53e3')), 38 | ('bandwidth', models.IntegerField(default=100, verbose_name='\u5e26\u5bbd')), 39 | ('priority', models.IntegerField(choices=[(0, '\u4f4e'), (1, '\u4e2d'), (2, '\u9ad8'), (3, '\u6781\u9ad8')], default=1, verbose_name='\u4f18\u5148\u7ea7')), 40 | ('output_path', models.CharField(blank=True, max_length=255, null=True, verbose_name='\u8f93\u51fa\u6587\u4ef6')), 41 | ('log_path', models.CharField(blank=True, max_length=255, null=True, verbose_name='\u65e5\u5fd7\u6587\u4ef6')), 42 | ('status', models.IntegerField(choices=[(0, '\u7b49\u5f85\u6267\u884c'), (1, '\u6b63\u5728\u6267\u884c'), (2, '\u6267\u884c\u5b8c\u6210'), (3, '\u6267\u884c\u9519\u8bef')], default=0, verbose_name='\u72b6\u6001')), 43 | ('progress', models.CharField(blank=True, max_length=3, null=True, verbose_name='\u6267\u884c\u8fdb\u5ea6')), 44 | ('left_time', models.CharField(blank=True, max_length=15, null=True, verbose_name='\u5269\u4f59\u65f6\u95f4')), 45 | ('verbosity', models.IntegerField(default=3, verbose_name='\u65e5\u5fd7\u7ea7\u522b')), 46 | ('creation_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='\u521b\u5efa\u65f6\u95f4')), 47 | ('start_time', models.DateTimeField(blank=True, null=True, verbose_name='\u542f\u52a8\u65f6\u95f4')), 48 | ('end_time', models.DateTimeField(blank=True, null=True, verbose_name='\u7ed3\u675f\u65f6\u95f4')), 49 | ('hit_rate', models.CharField(blank=True, max_length=10, null=True, verbose_name='\u547d\u4e2d\u7387')), 50 | ('remark', models.CharField(blank=True, max_length=255, null=True, verbose_name='\u5907\u6ce8')), 51 | ('black_list_file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.BlackListFile', verbose_name='\u9ed1\u540d\u5355')), 52 | ], 53 | options={ 54 | 'verbose_name': '\u4efb\u52a1', 55 | 'verbose_name_plural': '\u4efb\u52a1', 56 | }, 57 | ), 58 | migrations.CreateModel( 59 | name='WhiteListFile', 60 | fields=[ 61 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 62 | ('file', models.FileField(upload_to=core.models.get_white_list_path, verbose_name='\u6587\u4ef6')), 63 | ('size', models.BigIntegerField(default=0, verbose_name='\u5927\u5c0f')), 64 | ('remark', models.CharField(blank=True, max_length=255, null=True, verbose_name='\u5907\u6ce8')), 65 | ], 66 | options={ 67 | 'verbose_name': '\u767d\u540d\u5355', 68 | 'verbose_name_plural': '\u767d\u540d\u5355', 69 | }, 70 | ), 71 | migrations.AddField( 72 | model_name='job', 73 | name='white_list_file', 74 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.WhiteListFile', verbose_name='\u767d\u540d\u5355'), 75 | ), 76 | ] 77 | -------------------------------------------------------------------------------- /core/migrations/0002_job_status_path.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-06 02:00 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='job', 17 | name='status_path', 18 | field=models.CharField(blank=True, max_length=255, null=True, verbose_name='\u72b6\u6001\u6587\u4ef6'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0003_auto_20160706_1102.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-06 03:02 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0002_job_status_path'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='job', 17 | name='bandwidth', 18 | field=models.IntegerField(default=1, verbose_name='\u5e26\u5bbd'), 19 | ), 20 | migrations.AlterField( 21 | model_name='job', 22 | name='verbosity', 23 | field=models.IntegerField(choices=[(0, 'FATAL'), (1, 'ERROR'), (2, 'WARN'), (3, 'INFO'), (4, 'DEBUG'), (5, 'TRACE')], default=3, verbose_name='\u65e5\u5fd7\u7ea7\u522b'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /core/migrations/0004_auto_20160706_1703.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-06 09:03 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0003_auto_20160706_1102'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='job', 17 | name='active_send_threads', 18 | field=models.IntegerField(default=0), 19 | ), 20 | migrations.AddField( 21 | model_name='job', 22 | name='drop_avg_per_sec', 23 | field=models.IntegerField(default=0), 24 | ), 25 | migrations.AddField( 26 | model_name='job', 27 | name='drop_last_one_sec', 28 | field=models.IntegerField(default=0), 29 | ), 30 | migrations.AddField( 31 | model_name='job', 32 | name='pcap_drop_total', 33 | field=models.IntegerField(default=0), 34 | ), 35 | migrations.AddField( 36 | model_name='job', 37 | name='percent_complete', 38 | field=models.FloatField(default=0), 39 | ), 40 | migrations.AddField( 41 | model_name='job', 42 | name='read_time', 43 | field=models.CharField(blank=True, max_length=25, null=True), 44 | ), 45 | migrations.AddField( 46 | model_name='job', 47 | name='recv_success_avg_per_sec', 48 | field=models.IntegerField(default=0), 49 | ), 50 | migrations.AddField( 51 | model_name='job', 52 | name='recv_success_last_one_sec', 53 | field=models.IntegerField(default=0), 54 | ), 55 | migrations.AddField( 56 | model_name='job', 57 | name='recv_success_total', 58 | field=models.IntegerField(default=0), 59 | ), 60 | migrations.AddField( 61 | model_name='job', 62 | name='recv_total', 63 | field=models.IntegerField(default=0), 64 | ), 65 | migrations.AddField( 66 | model_name='job', 67 | name='recv_total_avg_per_sec', 68 | field=models.IntegerField(default=0), 69 | ), 70 | migrations.AddField( 71 | model_name='job', 72 | name='recv_total_last_one_sec', 73 | field=models.IntegerField(default=0), 74 | ), 75 | migrations.AddField( 76 | model_name='job', 77 | name='sendto_fail_avg_per_sec', 78 | field=models.IntegerField(default=0), 79 | ), 80 | migrations.AddField( 81 | model_name='job', 82 | name='sendto_fail_last_one_sec', 83 | field=models.IntegerField(default=0), 84 | ), 85 | migrations.AddField( 86 | model_name='job', 87 | name='sendto_fail_total', 88 | field=models.IntegerField(default=0), 89 | ), 90 | migrations.AddField( 91 | model_name='job', 92 | name='sent_avg_per_sec', 93 | field=models.IntegerField(default=0), 94 | ), 95 | migrations.AddField( 96 | model_name='job', 97 | name='sent_last_one_sec', 98 | field=models.IntegerField(default=0), 99 | ), 100 | migrations.AddField( 101 | model_name='job', 102 | name='sent_total', 103 | field=models.IntegerField(default=0), 104 | ), 105 | migrations.AddField( 106 | model_name='job', 107 | name='time_elapsed', 108 | field=models.IntegerField(default=0), 109 | ), 110 | migrations.AddField( 111 | model_name='job', 112 | name='time_remaining', 113 | field=models.IntegerField(default=0), 114 | ), 115 | ] 116 | -------------------------------------------------------------------------------- /core/migrations/0005_job_pid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-06 09:08 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0004_auto_20160706_1703'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='job', 17 | name='pid', 18 | field=models.IntegerField(blank=True, default=-1, null=True, verbose_name='\u8fdb\u7a0b\u53f7'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0006_auto_20160706_1710.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-06 09:10 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0005_job_pid'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='job', 17 | name='pid', 18 | field=models.IntegerField(blank=True, null=True, verbose_name='\u8fdb\u7a0b\u53f7'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0007_auto_20160706_1754.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-06 09:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0006_auto_20160706_1710'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='job', 17 | name='hit_rate', 18 | ), 19 | migrations.RemoveField( 20 | model_name='job', 21 | name='left_time', 22 | ), 23 | migrations.RemoveField( 24 | model_name='job', 25 | name='progress', 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /core/migrations/0008_auto_20160706_2354.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-07-06 15:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('core', '0007_auto_20160706_1754'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Command', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('command', models.IntegerField()), 21 | ('status', models.IntegerField()), 22 | ('job_id', models.CharField(max_length=32)), 23 | ('creation_time', models.DateTimeField(default=django.utils.timezone.now)), 24 | ], 25 | ), 26 | migrations.AlterField( 27 | model_name='job', 28 | name='read_time', 29 | field=models.DateTimeField(blank=True, null=True), 30 | ), 31 | migrations.AlterField( 32 | model_name='job', 33 | name='status', 34 | field=models.IntegerField(choices=[(0, '\u7b49\u5f85\u6267\u884c'), (1, '\u6b63\u5728\u6267\u884c'), (2, '\u6267\u884c\u5b8c\u6210'), (3, '\u6267\u884c\u9519\u8bef'), (4, '\u6682\u505c')], default=0, verbose_name='\u72b6\u6001'), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /core/migrations/0009_delete_command.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-07-06 15:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0008_auto_20160706_2354'), 12 | ] 13 | 14 | operations = [ 15 | migrations.DeleteModel( 16 | name='Command', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /core/migrations/0010_auto_20160707_0927.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-07 01:27 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0009_delete_command'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='blacklistfile', 17 | name='name', 18 | field=models.CharField(default=1, max_length=30, verbose_name='\u540d\u79f0'), 19 | preserve_default=False, 20 | ), 21 | migrations.AddField( 22 | model_name='whitelistfile', 23 | name='name', 24 | field=models.CharField(default=1, max_length=30, verbose_name='\u540d\u79f0'), 25 | preserve_default=False, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /core/migrations/0011_command.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-07 02:16 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | import django.utils.timezone 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('core', '0010_auto_20160707_0927'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Command', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('creation_time', models.DateTimeField(default=django.utils.timezone.now)), 22 | ('cmd', models.IntegerField(choices=[(0, 'PAUSE'), (1, 'CONTINUE'), (2, 'STOP')])), 23 | ('status', models.IntegerField(choices=[(0, 'WAIT'), (1, 'RUNNING'), (2, 'DONE'), (3, 'ERROR')])), 24 | ('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Job')), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /core/migrations/0012_auto_20160707_1655.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-07 08:55 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0011_command'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='command', 17 | options={'verbose_name': '\u547d\u4ee4', 'verbose_name_plural': '\u547d\u4ee4'}, 18 | ), 19 | migrations.AlterField( 20 | model_name='blacklistfile', 21 | name='name', 22 | field=models.CharField(max_length=30, unique=True, verbose_name='\u540d\u79f0'), 23 | ), 24 | migrations.AlterField( 25 | model_name='command', 26 | name='cmd', 27 | field=models.IntegerField(choices=[(0, 'PAUSE'), (1, 'CONTINUE'), (2, 'STOP'), (3, 'CANCEL')]), 28 | ), 29 | migrations.AlterField( 30 | model_name='job', 31 | name='status', 32 | field=models.IntegerField(choices=[(0, 'WAIT'), (1, 'RUNNING'), (2, 'DONE'), (3, 'ERROR'), (4, 'PAUSE'), (5, 'CANCELED'), (6, 'STOPPED')], default=0, verbose_name='\u72b6\u6001'), 33 | ), 34 | migrations.AlterField( 35 | model_name='whitelistfile', 36 | name='name', 37 | field=models.CharField(max_length=30, unique=True, verbose_name='\u540d\u79f0'), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /core/migrations/0013_auto_20160712_2235.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-12 14:35 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0012_auto_20160707_1655'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='command', 17 | name='status', 18 | field=models.IntegerField(choices=[(0, 'Pending'), (1, 'RUNNING'), (2, 'DONE'), (3, 'ERROR')]), 19 | ), 20 | migrations.AlterField( 21 | model_name='job', 22 | name='status', 23 | field=models.IntegerField(choices=[(0, 'PENDING'), (1, 'RUNNING'), (2, 'DONE'), (3, 'ERROR'), (4, 'PAUSE'), (5, 'CANCELED'), (6, 'STOPPED')], default=0, verbose_name='\u72b6\u6001'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /core/migrations/0014_auto_20160712_2307.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-12 15:07 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0013_auto_20160712_2235'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='command', 17 | name='status', 18 | field=models.IntegerField(choices=[(0, 'Pending'), (1, 'DONE'), (2, 'ERROR')]), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0015_auto_20160712_2307.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-12 15:07 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0014_auto_20160712_2307'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='command', 17 | name='cmd', 18 | field=models.IntegerField(choices=[(0, 'PAUSE'), (1, 'CONTINUE'), (2, 'STOP'), (3, 'CANCEL')], default=0), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0016_auto_20160712_2315.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-12 15:15 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0015_auto_20160712_2307'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='command', 17 | name='cmd', 18 | field=models.IntegerField(choices=[(0, 'PAUSE'), (1, 'CONTINUE'), (2, 'STOP')], default=0), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0017_auto_20160712_2340.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-12 15:40 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0016_auto_20160712_2315'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='command', 17 | name='cmd', 18 | field=models.IntegerField(choices=[(0, 'PAUSE'), (1, 'CONTINUE'), (2, 'STOP')]), 19 | ), 20 | migrations.AlterField( 21 | model_name='command', 22 | name='status', 23 | field=models.IntegerField(choices=[(0, 'Pending'), (1, 'DONE'), (2, 'ERROR')], default=0), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /core/migrations/0018_auto_20160713_0018.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-12 16:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0017_auto_20160712_2340'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='job', 17 | name='status', 18 | field=models.IntegerField(choices=[(0, 'PENDING'), (1, 'RUNNING'), (2, 'DONE'), (3, 'ERROR'), (4, 'PAUSE'), (5, 'STOPPED'), (6, 'CANCELED')], default=0, verbose_name='\u72b6\u6001'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0019_job_subnets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-13 08:17 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0018_auto_20160713_0018'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='job', 17 | name='subnets', 18 | field=models.CharField(blank=True, max_length=255, null=True, verbose_name='\u76ee\u6807\u5b50\u7f51'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/0020_auto_20160825_0944.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10 on 2016-08-25 01:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('core', '0019_job_subnets'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='job', 17 | name='bandwidth', 18 | field=models.IntegerField(default=2, verbose_name='\u5e26\u5bbd'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/core/migrations/__init__.py -------------------------------------------------------------------------------- /core/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from __future__ import unicode_literals 4 | 5 | from django.db import models 6 | from django.utils import timezone 7 | from webzmap.settings import WORK_DIR 8 | import default as settings 9 | import os 10 | import uuid 11 | 12 | 13 | def get_white_list_path(self, filename): 14 | parent = os.path.join(WORK_DIR, "whitelist") 15 | return os.path.join(parent, filename) 16 | 17 | 18 | def get_black_list_path(self, filename): 19 | parent = os.path.join(WORK_DIR, "blacklist") 20 | return os.path.join(parent, filename) 21 | 22 | 23 | def gen_job_id(): 24 | return str(uuid.uuid4()).replace('-', '') 25 | 26 | 27 | class BlackListFile(models.Model): 28 | """ 29 | Zmap black list file 30 | """ 31 | name = models.CharField(max_length=30, unique=True, verbose_name=u'名称') 32 | file = models.FileField(upload_to=get_black_list_path, verbose_name='文件') 33 | size = models.BigIntegerField(default=0, verbose_name=u'大小') 34 | remark = models.CharField(max_length=255, verbose_name=u'备注', blank=True, null=True) 35 | 36 | class Meta: 37 | verbose_name = '黑名单' 38 | verbose_name_plural = '黑名单' 39 | 40 | def __unicode__(self): 41 | return self.name 42 | 43 | 44 | class WhiteListFile(models.Model): 45 | """ 46 | Zmap white list file 47 | """ 48 | name = models.CharField(max_length=30, unique=True, verbose_name=u'名称') 49 | file = models.FileField(upload_to=get_white_list_path, verbose_name='文件') 50 | size = models.BigIntegerField(default=0, verbose_name=u'大小') 51 | remark = models.CharField(max_length=255, verbose_name=u'备注', blank=True, null=True) 52 | 53 | class Meta: 54 | verbose_name = '白名单' 55 | verbose_name_plural = '白名单' 56 | 57 | def __unicode__(self): 58 | return self.name 59 | 60 | 61 | class Job(models.Model): 62 | STATUS_PENDING = 0 63 | STATUS_RUNNING = 1 64 | STATUS_DONE = 2 65 | STATUS_ERROR = 3 66 | STATUS_PAUSED = 4 67 | STATUS_STOPPED = 5 68 | STATUS_CANCELED = 6 69 | STATUS_CHOICES = ( 70 | (STATUS_PENDING, u'PENDING'), 71 | (STATUS_RUNNING, u'RUNNING'), 72 | (STATUS_DONE, u'DONE'), 73 | (STATUS_ERROR, u'ERROR'), 74 | (STATUS_PAUSED, u'PAUSE'), 75 | (STATUS_STOPPED, u'STOPPED'), 76 | (STATUS_CANCELED, u'CANCELED'), 77 | ) 78 | PRIORITY = ( 79 | (0, u'低'), 80 | (1, u'中'), 81 | (2, u'高'), 82 | (3, u'极高'), 83 | ) 84 | 85 | LOG_LEVEL = ( 86 | (0, 'FATAL'), 87 | (1, 'ERROR'), 88 | (2, 'WARN'), 89 | (3, 'INFO'), 90 | (4, 'DEBUG'), 91 | (5, 'TRACE'), 92 | ) 93 | 94 | id = models.CharField(max_length=32, default=gen_job_id, primary_key=True, verbose_name=u'ID') 95 | name = models.CharField(max_length=30, default=u'扫描任务', verbose_name=u'任务名称') 96 | port = models.IntegerField(verbose_name=u'扫描端口') 97 | subnets = models.CharField(max_length=255, null=True, blank=True, verbose_name=u'目标子网') 98 | bandwidth = models.IntegerField(default=settings.default_bandwidth, verbose_name=u'带宽') 99 | priority = models.IntegerField(choices=PRIORITY, default=1, verbose_name=u'优先级') 100 | white_list_file = models.ForeignKey(WhiteListFile, null=True, blank=True, verbose_name='白名单') 101 | black_list_file = models.ForeignKey(BlackListFile, null=True, blank=True, verbose_name=u'黑名单') 102 | output_path = models.CharField(max_length=255, null=True, blank=True, verbose_name=u'输出文件') 103 | log_path = models.CharField(max_length=255, null=True, blank=True, verbose_name=u'日志文件') 104 | status_path = models.CharField(max_length=255, null=True, blank=True, verbose_name=u'状态文件') 105 | status = models.IntegerField(choices=STATUS_CHOICES, default=0, verbose_name=u'状态') 106 | verbosity = models.IntegerField(choices=LOG_LEVEL, default=3, verbose_name=u'日志级别') 107 | creation_time = models.DateTimeField(default=timezone.now, verbose_name=u'创建时间') 108 | start_time = models.DateTimeField(null=True, blank=True, verbose_name=u'启动时间') 109 | end_time = models.DateTimeField(null=True, blank=True, verbose_name=u'结束时间') 110 | remark = models.CharField(max_length=255, verbose_name=u'备注', null=True, blank=True) 111 | 112 | pid = models.IntegerField(null=True, blank=True, verbose_name=u'进程号') 113 | read_time = models.DateTimeField(null=True, blank=True) 114 | time_elapsed = models.IntegerField(default=0) 115 | time_remaining = models.IntegerField(default=0) 116 | percent_complete = models.FloatField(default=0) 117 | active_send_threads = models.IntegerField(default=0) 118 | sent_total = models.IntegerField(default=0) 119 | sent_last_one_sec = models.IntegerField(default=0) 120 | sent_avg_per_sec = models.IntegerField(default=0) 121 | recv_success_total = models.IntegerField(default=0) 122 | recv_success_last_one_sec = models.IntegerField(default=0) 123 | recv_success_avg_per_sec = models.IntegerField(default=0) 124 | recv_total = models.IntegerField(default=0) 125 | recv_total_last_one_sec = models.IntegerField(default=0) 126 | recv_total_avg_per_sec = models.IntegerField(default=0) 127 | pcap_drop_total = models.IntegerField(default=0) 128 | drop_last_one_sec = models.IntegerField(default=0) 129 | drop_avg_per_sec = models.IntegerField(default=0) 130 | sendto_fail_total = models.IntegerField(default=0) 131 | sendto_fail_last_one_sec = models.IntegerField(default=0) 132 | sendto_fail_avg_per_sec = models.IntegerField(default=0) 133 | 134 | def update_execute_status(self, status): 135 | self.read_time = status.read_time 136 | self.time_elapsed = status.time_elapsed 137 | self.time_remaining = status.time_remaining 138 | self.percent_complete = status.percent_complete 139 | self.active_send_threads = status.active_send_threads 140 | self.sent_total = status.sent_total 141 | self.sent_last_one_sec = status.sent_last_one_sec 142 | self.sent_avg_per_sec = status.sent_avg_per_sec 143 | self.recv_success_total = status.recv_success_total 144 | self.recv_success_last_one_sec = status.recv_success_last_one_sec 145 | self.recv_success_avg_per_sec = status.recv_success_avg_per_sec 146 | self.recv_total = status.recv_total 147 | self.recv_total_last_one_sec = status.recv_total_last_one_sec 148 | self.recv_total_avg_per_sec = status.recv_total_avg_per_sec 149 | self.pcap_drop_total = status.pcap_drop_total 150 | self.drop_last_one_sec = status.drop_last_one_sec 151 | self.drop_avg_per_sec = status.drop_avg_per_sec 152 | self.sendto_fail_total = status.sendto_fail_total 153 | self.sendto_fail_last_one_sec = status.sendto_fail_last_one_sec 154 | self.sendto_fail_avg_per_sec = status.sendto_fail_avg_per_sec 155 | 156 | def hit_rate(self): 157 | return self.recv_success_total / self.sent_total 158 | 159 | class Meta: 160 | verbose_name = '任务' 161 | verbose_name_plural = '任务' 162 | 163 | def __unicode__(self): 164 | return self.name 165 | 166 | 167 | class Command(models.Model): 168 | STATUS_PENDING = 0 169 | STATUS_DONE = 1 170 | STATUS_ERROR = 2 171 | STATUS_TYPES = ( 172 | (STATUS_PENDING, u'Pending'), 173 | (STATUS_DONE, u'DONE'), 174 | (STATUS_ERROR, u'ERROR'), 175 | ) 176 | CMD_PAUSE = 0 177 | CMD_CONTINUE = 1 178 | CMD_STOP = 2 179 | CMD_TYPES = ( 180 | (CMD_PAUSE, u'PAUSE'), 181 | (CMD_CONTINUE, u'CONTINUE'), 182 | (CMD_STOP, u'STOP'), 183 | ) 184 | job = models.ForeignKey(Job, on_delete=models.CASCADE) 185 | creation_time = models.DateTimeField(default=timezone.now) 186 | cmd = models.IntegerField(choices=CMD_TYPES) 187 | status = models.IntegerField(choices=STATUS_TYPES, default=STATUS_PENDING) 188 | 189 | class Meta: 190 | verbose_name = '命令' 191 | verbose_name_plural = '命令' 192 | -------------------------------------------------------------------------------- /core/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /core/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | 4 | urlpatterns = [ 5 | url(r'^$', views.index, name='index', ), 6 | url(r'^login$', views.login, name='login'), 7 | url(r'^logout$', views.logout, name='logout'), 8 | ] 9 | -------------------------------------------------------------------------------- /core/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.contrib.auth.decorators import login_required 3 | from django.views.decorators.http import require_http_methods 4 | from django.contrib import auth 5 | from django.http import HttpResponse, HttpResponseRedirect 6 | import json 7 | 8 | 9 | # Create your views here. 10 | 11 | @login_required(login_url='/login') 12 | def index(request): 13 | return render(request, 'index.html') 14 | 15 | 16 | @require_http_methods(['GET', 'POST']) 17 | def login(request): 18 | method = request.method 19 | if method == 'GET': 20 | next_url = request.GET.get("next", "/") 21 | return render(request, 'login.html', context={"next": next_url}) 22 | else: 23 | username = request.POST.get("username") 24 | password = request.POST.get("password") 25 | next_url = request.POST.get("next", "/") 26 | user = auth.authenticate(username=username, password=password) 27 | if user and user.is_active: 28 | auth.login(request, user) 29 | return HttpResponseRedirect(next_url) 30 | else: 31 | return render(request, 'login.html', 32 | context={'error': "Username and password doesn't match", 'next': next_url}) 33 | 34 | 35 | def logout(request): 36 | auth.logout(request) 37 | return HttpResponseRedirect("/login") 38 | -------------------------------------------------------------------------------- /core/zmapd.py: -------------------------------------------------------------------------------- 1 | from core.models import Job, Command 2 | from django.utils import timezone 3 | from tools.zmap import Zmap, get_current_status 4 | from webzmap.settings import WORK_DIR 5 | from django import db 6 | import default as settings 7 | import logging 8 | import os 9 | import time 10 | import multiprocessing 11 | import signal 12 | import sys 13 | import fcntl 14 | 15 | logger = logging.getLogger('zmapd') 16 | 17 | 18 | class ProcessStatus(object): 19 | def __init__(self, running=False, pid=-1): 20 | self.running = running 21 | self.pid = pid 22 | 23 | 24 | def execute_job(job_id): 25 | job = Job.objects.get(id=job_id) 26 | job_home_path = os.path.join(WORK_DIR, job.id) 27 | if not os.path.exists(job_home_path): 28 | os.makedirs(job_home_path) 29 | job.status = Job.STATUS_RUNNING 30 | job.start_time = timezone.now() 31 | job.save() 32 | logger.info(u"running job: id[%s], name[%s]", job.id, job.name) 33 | output_path = os.path.join(job_home_path, 'output.txt') 34 | log_path = os.path.join(job_home_path, 'job.log') 35 | status_path = os.path.join(job_home_path, 'status.txt') 36 | zmap = Zmap(cwd=settings.cwd, execute_bin=settings.zmap_path) 37 | process = zmap.scan(job.port, subnets=job.subnets, output_path=output_path, log_path=log_path, 38 | verbosity=job.verbosity, bandwidth=job.bandwidth, status_updates_path=status_path, 39 | stderr=open("/dev/null")) 40 | job.pid = process.pid 41 | job.save() 42 | exit_code = process.poll() 43 | exit_by_user = False 44 | while exit_code is None: 45 | # check job is deleted 46 | try: 47 | Job.objects.get(id=job.id) 48 | except Job.DoesNotExist: 49 | process.send_signal(signal.SIGKILL) 50 | exit_by_user = True 51 | logger.info("stopped deleted job:id[%s] name[%s]", job.id, job.name) 52 | time.sleep(1) 53 | exit_code = process.poll() 54 | continue 55 | commands = Command.objects.filter(job=job, status=Command.STATUS_PENDING).order_by('creation_time') 56 | for command in commands: 57 | logger.info("execute command on job:[%s], type:%s", command.job.id, command.cmd) 58 | if command.cmd == Command.CMD_PAUSE: 59 | if job.status == Job.STATUS_RUNNING: 60 | process.send_signal(signal.SIGSTOP) 61 | command.status = Command.STATUS_DONE 62 | command.save() 63 | job.status = Job.STATUS_PAUSED 64 | job.save() 65 | else: 66 | command.status = Command.STATUS_ERROR 67 | command.save() 68 | if command.cmd == Command.CMD_CONTINUE: 69 | if job.status == Job.STATUS_PAUSED: 70 | process.send_signal(signal.SIGCONT) 71 | command.status = Command.STATUS_DONE 72 | command.save() 73 | job.status = Job.STATUS_RUNNING 74 | job.save() 75 | else: 76 | command.status = Command.STATUS_ERROR 77 | command.save() 78 | if command.cmd == Command.CMD_STOP: 79 | if job.status == Job.STATUS_RUNNING or job.status == Job.STATUS_PAUSED: 80 | process.send_signal(signal.SIGKILL) 81 | command.status = Command.STATUS_DONE 82 | command.save() 83 | job.status = Job.STATUS_STOPPED 84 | job.end_time = timezone.now() 85 | job.save() 86 | exit_by_user = True 87 | try: 88 | status = get_current_status(status_path) 89 | if status: 90 | job.update_execute_status(status) 91 | job.save() 92 | except ValueError: 93 | pass 94 | time.sleep(1) 95 | exit_code = process.poll() 96 | if exit_code == 0: 97 | logger.info("zmap return success code:%s, log path:%s", exit_code, log_path) 98 | job.status = Job.STATUS_DONE 99 | job.percent_complete = 100 100 | job.end_time = timezone.now() 101 | job.time_remaining = 0 102 | job.save() 103 | elif not exit_by_user: 104 | logger.error("zmap return error code:%s, log path:%s", exit_code, log_path) 105 | job.status = Job.STATUS_ERROR 106 | job.end_time = timezone.now() 107 | job.save() 108 | 109 | 110 | def start(): 111 | if os.path.exists(settings.pid_file): 112 | with open(settings.pid_file) as f: 113 | try: 114 | fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) 115 | fcntl.flock(f, fcntl.LOCK_UN) 116 | except IOError: 117 | sys.stdout.write("zmapd is already started\n") 118 | return 119 | 120 | run_daemon_process(pid_file=settings.pid_file, start_msg="Start zmapd(%s)\n") 121 | pid_file = open(settings.pid_file) 122 | fcntl.flock(pid_file, fcntl.LOCK_SH) 123 | while True: 124 | time.sleep(1) 125 | running_jobs = Job.objects.filter(status=Job.STATUS_RUNNING) 126 | total_bandwidth = 0 127 | for job in running_jobs: 128 | total_bandwidth += job.bandwidth 129 | if total_bandwidth >= settings.max_bandwidth: 130 | logger.debug(u"Achieve maximum bandwidth:%sM", settings.max_bandwidth) 131 | continue 132 | jobs = [x for x in Job.objects.filter(status=Job.STATUS_PENDING).order_by('-priority')] 133 | db.close_old_connections() 134 | for j in jobs: 135 | p = multiprocessing.Process(target=execute_job, args=(j.id,)) 136 | p.start() 137 | 138 | 139 | def status(): 140 | process_status = get_process_status() 141 | if process_status.running: 142 | sys.stdout.write('zmapd(pid %d) is running...\n' % process_status.pid) 143 | else: 144 | sys.stdout.write("zmapd is stopped\n") 145 | 146 | 147 | def stop(): 148 | sys.stdout.write("Stopping zmapd...") 149 | process_status = get_process_status() 150 | if process_status.running: 151 | with open(settings.pid_file, 'r') as f: 152 | pid = int(f.readline()) 153 | try: 154 | os.kill(pid, signal.SIGTERM) 155 | except OSError: 156 | sys.stdout.write(" [FAILED]\n") 157 | sys.stdout.write("zmapd is not running\n") 158 | os.remove(settings.pid_file) 159 | sys.stdout.write(" [OK]\n") 160 | else: 161 | sys.stdout.write(" [FAILED]\n") 162 | sys.stdout.write("zmapd is not running\n") 163 | 164 | 165 | def restart(): 166 | stop() 167 | start() 168 | 169 | 170 | def get_process_status(): 171 | if os.path.exists(settings.pid_file): 172 | with open(settings.pid_file) as f: 173 | try: 174 | fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) 175 | fcntl.flock(f, fcntl.LOCK_UN) 176 | os.remove(settings.pid_file) 177 | return ProcessStatus(False) 178 | except IOError: 179 | pid = int(f.readline()) 180 | return ProcessStatus(True, pid) 181 | else: 182 | return ProcessStatus(False) 183 | 184 | 185 | def run_daemon_process(stdout='/dev/null', stderr=None, stdin='/dev/null', 186 | pid_file=None, start_msg='started with pid %s'): 187 | """ 188 | This forks the current process into a daemon. 189 | The stdin, stdout, and stderr arguments are file names that 190 | will be opened and be used to replace the standard file descriptors 191 | in sys.stdin, sys.stdout, and sys.stderr. 192 | These arguments are optional and default to /dev/null. 193 | Note that stderr is opened unbuffered, so 194 | if it shares a file with stdout then interleaved output 195 | may not appear in the order that you expect. 196 | """ 197 | # flush io 198 | sys.stdout.flush() 199 | sys.stderr.flush() 200 | # Do first fork. 201 | try: 202 | if os.fork() > 0: 203 | sys.exit(0) # Exit first parent. 204 | except OSError, e: 205 | sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) 206 | sys.exit(1) 207 | # Decouple from parent environment. 208 | os.chdir("/") 209 | os.umask(0) 210 | os.setsid() 211 | # Do second fork. 212 | try: 213 | if os.fork() > 0: 214 | sys.exit(0) # Exit second parent. 215 | except OSError, e: 216 | sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) 217 | sys.exit(1) 218 | # Open file descriptors and print start message 219 | if not stderr: 220 | stderr = stdout 221 | si = file(stdin, 'r') 222 | so = file(stdout, 'a+') 223 | se = file(stderr, 'a+', 0) # unbuffered 224 | pid = str(os.getpid()) 225 | sys.stderr.write(start_msg % pid) 226 | sys.stderr.flush() 227 | if pid_file: 228 | with open(pid_file, 'w+') as f: 229 | f.write("%s\n" % pid) 230 | # Redirect standard file descriptors. 231 | os.dup2(si.fileno(), sys.stdin.fileno()) 232 | os.dup2(so.fileno(), sys.stdout.fileno()) 233 | os.dup2(se.fileno(), sys.stderr.fileno()) 234 | return pid 235 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webzmap.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.10 2 | requests==2.9.1 3 | djangorestframework==3.4.4 -------------------------------------------------------------------------------- /static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/static/img/icon.png -------------------------------------------------------------------------------- /static/img/webzmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/static/img/webzmap.png -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- 1 | jobList = null; 2 | 3 | function readableTime(seconds) { 4 | if(isNaN(seconds)){ 5 | return "-"; 6 | } 7 | day = Math.floor(seconds / 86400); 8 | hour = Math.floor((seconds - day * 86400) / 3600); 9 | minute = Math.floor((seconds - day * 86400 - hour * 3600 ) / 60); 10 | second = seconds - day * 86400 - hour * 3600 - minute * 60 11 | if(hour < 10){ 12 | hour = "0" + hour; 13 | } 14 | if(minute < 10){ 15 | minute = "0" + minute; 16 | } 17 | if(second < 10){ 18 | second = "0" + second; 19 | } 20 | if(day == 0){ 21 | return hour + ":" + minute + ":" + second; 22 | }else{ 23 | return day + "d " + hour + ":" + minute + ":" + second; 24 | } 25 | } 26 | 27 | function percentFormat(number) { 28 | if(isNaN(number)){ 29 | return "-"; 30 | } 31 | number = number * 100; 32 | var percent =number.toFixed(2); 33 | return percent+"%"; 34 | } 35 | 36 | template.helper('percentFormat', percentFormat); 37 | 38 | template.helper('readableTime',readableTime); 39 | 40 | function getCookie(name) { 41 | var cookieValue = null; 42 | if (document.cookie && document.cookie != '') { 43 | var cookies = document.cookie.split(';'); 44 | for (var i = 0; i < cookies.length; i++) { 45 | var cookie = jQuery.trim(cookies[i]); 46 | // Does this cookie string begin with the name we want? 47 | if (cookie.substring(0, name.length + 1) == (name + '=')) { 48 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 49 | break; 50 | } 51 | } 52 | } 53 | return cookieValue; 54 | } 55 | 56 | function csrfSafeMethod(method) { 57 | // these HTTP methods do not require CSRF protection 58 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 59 | } 60 | 61 | function sameOrigin(url) { 62 | // test that a given url is a same-origin URL 63 | // url could be relative or scheme relative or absolute 64 | var host = document.location.host; // host + port 65 | var protocol = document.location.protocol; 66 | var sr_origin = '//' + host; 67 | var origin = protocol + sr_origin; 68 | // Allow absolute or scheme relative URLs to same origin 69 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || 70 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || 71 | // or any other URL that isn't scheme relative or absolute i.e relative. 72 | !(/^(\/\/|http:|https:).*/.test(url)); 73 | } 74 | 75 | 76 | function showAllJobs(){ 77 | $.get("api/jobs", function(result){ 78 | jobList = result; 79 | $("#tableBody").empty(); 80 | $.each(result,function(n,value) { 81 | var data = {job: value, index:n} 82 | var html = template('jobRow', data); 83 | $("#tableBody").append(html); 84 | }); 85 | }); 86 | } 87 | 88 | 89 | function getJobOptions(){ 90 | var aj = $.ajax( { 91 | url:'/api/jobs', 92 | type:'options', 93 | cache:false, 94 | dataType:'json', 95 | success:function(data) { 96 | $("#selectWhiteListFile").empty(); 97 | $("#selectWhiteListFile").append(template('fileOption', {file:{value:null,display_name:''}})) 98 | $("#selectBlackListFile").empty(); 99 | $("#selectBlackListFile").append(template('fileOption', {file:{value:null,display_name:''}})) 100 | console.log("options success") 101 | console.log(data) 102 | whiteList = data.actions.POST.white_list_file.choices 103 | $.each(whiteList,function(n,file) { 104 | var data = {file: file} 105 | var html = template('fileOption', data); 106 | $('#selectWhiteListFile').append(html); 107 | }); 108 | blackList = data.actions.POST.black_list_file.choices 109 | $.each(blackList,function(n,file) { 110 | var data = {file: file} 111 | var html = template('fileOption', data); 112 | $('#selectBlackListFile').append(html); 113 | }); 114 | }, 115 | error : function(request) { 116 | var data = eval('(' + request.responseText + ')'); 117 | alert(data.detail); 118 | } 119 | }); 120 | } 121 | 122 | 123 | $('#newJobModal').on('show.bs.modal', 124 | function(event) { 125 | getJobOptions(); 126 | }); 127 | 128 | $('#newJobModal').on('hide.bs.modal', 129 | function(event) { 130 | }); 131 | 132 | 133 | function createJob(){ 134 | var csrftoken = getCookie('csrftoken'); 135 | $("#createJobButton").addClass('disabled'); 136 | $.ajax({ 137 | cache: true, 138 | type: "POST", 139 | url:'/api/jobs/', 140 | headers:{'X-CSRFToken':csrftoken}, 141 | data:$('#newJobForm').serialize(), 142 | async: true, 143 | success: function(data) { 144 | $("#createJobButton").removeClass('disabled'); 145 | $('#newJobModal').modal('hide'); 146 | showAllJobs(); 147 | }, 148 | error: function(request) { 149 | $("#createJobButton").removeClass('disabled'); 150 | console.log(request) 151 | var data = eval('(' + request.responseText + ')'); 152 | for(var key in data){ 153 | alert(key+':'+data[key]) 154 | } 155 | } 156 | }); 157 | } 158 | 159 | function deleteJob(url){ 160 | var csrftoken = getCookie('csrftoken'); 161 | var aj = $.ajax( { 162 | url:url, 163 | type:'DELETE', 164 | data:$('#deleteJobForm').serialize(), 165 | headers:{'X-CSRFToken':csrftoken}, 166 | async: true, 167 | cache:false, 168 | dataType:'json', 169 | success:function(data) { 170 | console.log(data) 171 | $('#deleteJobModal').modal('hide'); 172 | showAllJobs(); 173 | }, 174 | error : function(request) { 175 | console.log(request) 176 | var data = eval('(' + request.responseText + ')'); 177 | alert(data.detail); 178 | } 179 | }); 180 | } 181 | 182 | 183 | function showDeleteJobModal(url){ 184 | $('#deleteJobModal').modal('show'); 185 | $('#deleteButton').attr('onclick',"deleteJob('"+url+"')"); 186 | } 187 | 188 | function showLog(id){ 189 | $('#logModal').modal('show'); 190 | url = '/static/' + id + '/job.log'; 191 | $('#logContainer').empty(); 192 | var aj = $.ajax( { 193 | url:url, 194 | type:'GET', 195 | cache:false, 196 | dataType:'text', 197 | success:function(data) { 198 | console.log(data) 199 | $('#logContainer').append(data); 200 | }, 201 | error : function(request) { 202 | console.log(request) 203 | $('#logContainer').append("Get log failed"); 204 | } 205 | }); 206 | } 207 | 208 | function updateJobStatus(){ 209 | $.get("api/jobs", function(result){ 210 | jobList = result; 211 | $.each(result,function(n,job) { 212 | id = job.id; 213 | nameId = "#"+id+"-name"; 214 | progressId = "#" + id + "-progress"; 215 | statusId = "#" + id + "-status"; 216 | hitId = "#" + id + "-hit"; 217 | hitRateId = "#" + id + "-hitRate"; 218 | leftId = "#" + id + "-left"; 219 | logId = "#" + id + "-log"; 220 | downloadId = "#" + id + "-download"; 221 | operationId = "#" + id+ "-operation"; 222 | $(nameId).html(job.name); 223 | $(progressId).attr("style", "width: "+job.percent_complete+"%"); 224 | $(progressId).attr("aria-valuenow", job.percent_complete); 225 | if(job.status == 1){ 226 | $(progressId).addClass("active"); 227 | $(leftId).html(readableTime(job.time_remaining)); 228 | }else{ 229 | $(progressId).removeClass("active"); 230 | $(leftId).html("-"); 231 | } 232 | if(job.status==0){ 233 | $(logId).attr("disabled", "disabled"); 234 | $(downloadId).attr("disabled", "disabled"); 235 | $(downloadId).attr("href", "#"); 236 | }else{ 237 | $(logId).removeAttr("disabled"); 238 | $(downloadId).removeAttr("disabled"); 239 | $(downloadId).attr("href", "/static/"+job.id+"/output.txt"); 240 | } 241 | if(job.status == 1){ 242 | $(operationId).attr("class", "glyphicon glyphicon-pause"); 243 | $(operationId).attr("onclick", "createCommand('"+job.url+"', 0)"); 244 | }else if(job.status== 4){ 245 | $(operationId).attr("class", "glyphicon glyphicon-play"); 246 | $(operationId).attr("onclick", "createCommand('"+job.url+"', 1)"); 247 | }else{ 248 | $(operationId).attr("class", "glyphicon glyphicon-repeat"); 249 | } 250 | var data = {status:job.status} 251 | var html = template('statusTemplate', data); 252 | $(statusId).html(html); 253 | $(hitId).html(job.recv_success_total); 254 | $(hitRateId).html(percentFormat(job.recv_success_total/job.sent_total)); 255 | }); 256 | }); 257 | } 258 | 259 | function createCommand(job_url, cmd){ 260 | var csrftoken = getCookie('csrftoken'); 261 | data = {'job':job_url, cmd:cmd} 262 | var aj = $.ajax( { 263 | url:'/api/commands/', 264 | type:'POST', 265 | data: data, 266 | headers:{'X-CSRFToken':csrftoken}, 267 | async: true, 268 | cache:false, 269 | dataType:'json', 270 | success:function(data) { 271 | 272 | }, 273 | error : function(request) { 274 | console.log(request) 275 | var data = eval('(' + request.responseText + ')'); 276 | alert(data.detail); 277 | } 278 | }); 279 | } 280 | 281 | function autoUpdateStatus(time){ 282 | updateJobStatus(); 283 | setTimeout("autoUpdateStatus("+time+")", time ) 284 | } 285 | 286 | $(document).ready(function(){ 287 | showAllJobs(); 288 | autoUpdateStatus(1000); 289 | }); -------------------------------------------------------------------------------- /static/js/template.js: -------------------------------------------------------------------------------- 1 | /*!art-template - Template Engine | http://aui.github.com/artTemplate/*/ 2 | !function(){function a(a){return a.replace(t,"").replace(u,",").replace(v,"").replace(w,"").replace(x,"").split(y)}function b(a){return"'"+a.replace(/('|\\)/g,"\\$1").replace(/\r/g,"\\r").replace(/\n/g,"\\n")+"'"}function c(c,d){function e(a){return m+=a.split(/\n/).length-1,k&&(a=a.replace(/\s+/g," ").replace(//g,"")),a&&(a=s[1]+b(a)+s[2]+"\n"),a}function f(b){var c=m;if(j?b=j(b,d):g&&(b=b.replace(/\n/g,function(){return m++,"$line="+m+";"})),0===b.indexOf("=")){var e=l&&!/^=[=#]/.test(b);if(b=b.replace(/^=[=#]?|[\s;]*$/g,""),e){var f=b.replace(/\s*\([^\)]+\)/,"");n[f]||/^(include|print)$/.test(f)||(b="$escape("+b+")")}else b="$string("+b+")";b=s[1]+b+s[2]}return g&&(b="$line="+c+";"+b),r(a(b),function(a){if(a&&!p[a]){var b;b="print"===a?u:"include"===a?v:n[a]?"$utils."+a:o[a]?"$helpers."+a:"$data."+a,w+=a+"="+b+",",p[a]=!0}}),b+"\n"}var g=d.debug,h=d.openTag,i=d.closeTag,j=d.parser,k=d.compress,l=d.escape,m=1,p={$data:1,$filename:1,$utils:1,$helpers:1,$out:1,$line:1},q="".trim,s=q?["$out='';","$out+=",";","$out"]:["$out=[];","$out.push(",");","$out.join('')"],t=q?"$out+=text;return $out;":"$out.push(text);",u="function(){var text=''.concat.apply('',arguments);"+t+"}",v="function(filename,data){data=data||$data;var text=$utils.$include(filename,data,$filename);"+t+"}",w="'use strict';var $utils=this,$helpers=$utils.$helpers,"+(g?"$line=0,":""),x=s[0],y="return new String("+s[3]+");";r(c.split(h),function(a){a=a.split(i);var b=a[0],c=a[1];1===a.length?x+=e(b):(x+=f(b),c&&(x+=e(c)))});var z=w+x+y;g&&(z="try{"+z+"}catch(e){throw {filename:$filename,name:'Render Error',message:e.message,line:$line,source:"+b(c)+".split(/\\n/)[$line-1].replace(/^\\s+/,'')};}");try{var A=new Function("$data","$filename",z);return A.prototype=n,A}catch(B){throw B.temp="function anonymous($data,$filename) {"+z+"}",B}}var d=function(a,b){return"string"==typeof b?q(b,{filename:a}):g(a,b)};d.version="3.0.0",d.config=function(a,b){e[a]=b};var e=d.defaults={openTag:"<%",closeTag:"%>",escape:!0,cache:!0,compress:!1,parser:null},f=d.cache={};d.render=function(a,b){return q(a,b)};var g=d.renderFile=function(a,b){var c=d.get(a)||p({filename:a,name:"Render Error",message:"Template not found"});return b?c(b):c};d.get=function(a){var b;if(f[a])b=f[a];else if("object"==typeof document){var c=document.getElementById(a);if(c){var d=(c.value||c.innerHTML).replace(/^\s*|\s*$/g,"");b=q(d,{filename:a})}}return b};var h=function(a,b){return"string"!=typeof a&&(b=typeof a,"number"===b?a+="":a="function"===b?h(a.call(a)):""),a},i={"<":"<",">":">",'"':""","'":"'","&":"&"},j=function(a){return i[a]},k=function(a){return h(a).replace(/&(?![\w#]+;)|[<>"']/g,j)},l=Array.isArray||function(a){return"[object Array]"==={}.toString.call(a)},m=function(a,b){var c,d;if(l(a))for(c=0,d=a.length;d>c;c++)b.call(a,a[c],c,a);else for(c in a)b.call(a,a[c],c)},n=d.utils={$helpers:{},$include:g,$string:h,$escape:k,$each:m};d.helper=function(a,b){o[a]=b};var o=d.helpers=n.$helpers;d.onerror=function(a){var b="Template Error\n\n";for(var c in a)b+="<"+c+">\n"+a[c]+"\n\n";"object"==typeof console&&console.error(b)};var p=function(a){return d.onerror(a),function(){return"{Template Error}"}},q=d.compile=function(a,b){function d(c){try{return new i(c,h)+""}catch(d){return b.debug?p(d)():(b.debug=!0,q(a,b)(c))}}b=b||{};for(var g in e)void 0===b[g]&&(b[g]=e[g]);var h=b.filename;try{var i=c(a,b)}catch(j){return j.filename=h||"anonymous",j.name="Syntax Error",p(j)}return d.prototype=i.prototype,d.toString=function(){return i.toString()},h&&b.cache&&(f[h]=d),d},r=n.$each,s="break,case,catch,continue,debugger,default,delete,do,else,false,finally,for,function,if,in,instanceof,new,null,return,switch,this,throw,true,try,typeof,var,void,while,with,abstract,boolean,byte,char,class,const,double,enum,export,extends,final,float,goto,implements,import,int,interface,long,native,package,private,protected,public,short,static,super,synchronized,throws,transient,volatile,arguments,let,yield,undefined",t=/\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|\s*\.\s*[$\w\.]+/g,u=/[^\w$]+/g,v=new RegExp(["\\b"+s.replace(/,/g,"\\b|\\b")+"\\b"].join("|"),"g"),w=/^\d[^,]*|,\d[^,]*/g,x=/^,+|,+$/g,y=/^$|,+/;e.openTag="{{",e.closeTag="}}";var z=function(a,b){var c=b.split(":"),d=c.shift(),e=c.join(":")||"";return e&&(e=", "+e),"$helpers."+d+"("+a+e+")"};e.parser=function(a){a=a.replace(/^\s/,"");var b=a.split(" "),c=b.shift(),e=b.join(" ");switch(c){case"if":a="if("+e+"){";break;case"else":b="if"===b.shift()?" if("+b.join(" ")+")":"",a="}else"+b+"{";break;case"/if":a="}";break;case"each":var f=b[0]||"$data",g=b[1]||"as",h=b[2]||"$value",i=b[3]||"$index",j=h+","+i;"as"!==g&&(f="[]"),a="$each("+f+",function("+j+"){";break;case"/each":a="});";break;case"echo":a="print("+e+");";break;case"print":case"include":a=c+"("+b.join(",")+");";break;default:if(/^\s*\|\s*[\w\$]/.test(e)){var k=!0;0===a.indexOf("#")&&(a=a.substr(1),k=!1);for(var l=0,m=a.split("|"),n=m.length,o=m[l++];n>l;l++)o=z(o,m[l]);a=(k?"=":"=#")+o}else a=d.helpers[c]?"=#"+c+"("+b.join(",")+");":"="+a}return a},"function"==typeof define?define(function(){return d}):"undefined"!=typeof exports?module.exports=d:this.template=d}(); -------------------------------------------------------------------------------- /templates/admin/base_site.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base.html" %} 2 | 3 | {% block title %}WebZmap Admin{% endblock %} 4 | 5 | {% block branding %} 6 |

WebZmap Admin

7 | {% endblock %} 8 | 9 | {% block nav-global %}{% endblock %} 10 | 11 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 4 | 5 | {% block title %}WebZmap{% endblock %} 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | {% block body %}{% endblock %} 18 | 19 | 20 | 21 | {% block afterjs %}{% endblock %} 22 | 23 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %}{% load staticfiles %} 2 | {% block body %} 3 | 26 | 27 | 28 |
29 |

WebZmap

30 |
31 |
32 |
33 |
34 | 37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
#NameProgressStatusHitHit RateLeft TimeAction
58 |
59 |
60 |
61 |
62 | 63 | 64 | 125 | 126 | 127 | 145 | 146 | 147 | 164 | {% endblock %} 165 | {% block afterjs %} 166 | 167 | 210 | 221 | 224 | 225 | {% endblock %} -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %}{% load staticfiles %} 2 | {% block title %}Login - WebZmap{% endblock %} 3 | {% block body %} 4 |
5 |
6 |
7 | 8 |
9 |
10 |
11 | 12 |
13 | {% if error %}
{{error}}
{% endif %} 14 |
15 | {% csrf_token %} 16 | 17 |
18 | 19 | 21 |
22 |
23 | 24 | 26 |
27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 | {% endblock %} -------------------------------------------------------------------------------- /templates/rest_framework/base.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | {% load rest_framework %} 3 | {% load i18n %} 4 | 5 | 6 | 7 | 8 | {% block head %} 9 | 10 | {% block meta %} 11 | 12 | 13 | {% endblock %} 14 | 15 | {% block title %}{% if name %}{{ name }} – {% endif %}WebZmap REST API{% endblock %} 16 | 17 | {% block style %} 18 | {% block bootstrap_theme %} 19 | 20 | 21 | {% endblock %} 22 | 23 | 24 | 25 | {% endblock %} 26 | 27 | {% endblock %} 28 | 29 | 30 | {% block body %} 31 | 32 | 33 |
34 | {% block navbar %} 35 | 55 | {% endblock %} 56 | 57 |
58 | {% block breadcrumbs %} 59 | 68 | {% endblock %} 69 | 70 | 71 |
72 | 73 | {% if 'GET' in allowed_methods %} 74 |
75 |
76 | {% if api_settings.URL_FORMAT_OVERRIDE %} 77 |
78 | GET 79 | 80 | 83 | 90 |
91 | {% else %} 92 | GET 93 | {% endif %} 94 |
95 |
96 | {% endif %} 97 | 98 | {% if options_form %} 99 |
100 | 101 |
102 | {% endif %} 103 | 104 | {% if delete_form %} 105 | 106 | 107 | 108 | 123 | {% endif %} 124 | 125 | {% if filter_form %} 126 | 130 | {% endif %} 131 | 132 |
133 | 136 |
137 | {% block description %} 138 | {{ description }} 139 | {% endblock %} 140 |
141 | 142 | {% if paginator %} 143 | 146 | {% endif %} 147 | 148 |
149 |
{{ request.method }} {{ request.get_full_path }}
150 |
151 | 152 |
153 |
HTTP {{ response.status_code }} {{ response.status_text }}{% autoescape off %}
154 | {% for key, val in response_headers.items %}{{ key }}: {{ val|break_long_headers|urlize_quoted_links }}
155 | {% endfor %}
156 | {{ content|urlize_quoted_links }}
{% endautoescape %} 157 |
158 |
159 | 160 | {% if display_edit_forms %} 161 | 162 | {% if post_form or raw_data_post_form %} 163 |
164 | {% if post_form %} 165 | 173 | {% endif %} 174 | 175 |
176 | {% if post_form %} 177 |
178 | {% with form=post_form %} 179 |
180 |
181 | {% csrf_token %} 182 | {{ post_form }} 183 |
184 | 185 |
186 |
187 |
188 | {% endwith %} 189 |
190 | {% endif %} 191 | 192 |
193 | {% with form=raw_data_post_form %} 194 |
195 |
196 | {% include "rest_framework/raw_data_form.html" %} 197 |
198 | 199 |
200 |
201 |
202 | {% endwith %} 203 |
204 |
205 |
206 | {% endif %} 207 | 208 | {% if put_form or raw_data_put_form or raw_data_patch_form %} 209 |
210 | {% if put_form %} 211 | 219 | {% endif %} 220 | 221 |
222 | {% if put_form %} 223 |
224 |
225 |
226 | {{ put_form }} 227 |
228 | 229 |
230 |
231 |
232 |
233 | {% endif %} 234 | 235 |
236 | {% with form=raw_data_put_or_patch_form %} 237 |
238 |
239 | {% include "rest_framework/raw_data_form.html" %} 240 |
241 | {% if raw_data_put_form %} 242 | 243 | {% endif %} 244 | {% if raw_data_patch_form %} 245 | 246 | {% endif %} 247 |
248 |
249 |
250 | {% endwith %} 251 |
252 |
253 |
254 | {% endif %} 255 | {% endif %} 256 |
257 |
258 |
259 | 260 | {% block script %} 261 | 262 | 263 | 264 | 265 | 266 | 267 | 272 | {% endblock %} 273 | 274 | {% if filter_form %} 275 | {{ filter_form }} 276 | {% endif %} 277 | 278 | 279 | {% endblock %} 280 | 281 | -------------------------------------------------------------------------------- /templates/rest_framework/login.html: -------------------------------------------------------------------------------- 1 | {% extends "rest_framework/login_base.html" %} 2 | {% block branding %}

WebZmap REST API

{% endblock %} 3 | {# Override this template in your own templates directory to customize #} 4 | -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/tools/__init__.py -------------------------------------------------------------------------------- /tools/zmap.py: -------------------------------------------------------------------------------- 1 | from django.utils import timezone 2 | import os 3 | import subprocess 4 | import time 5 | import datetime 6 | 7 | 8 | class ZmapStatus(object): 9 | def __init__(self): 10 | self.read_time = None 11 | self.time_elapsed = 0 12 | self.time_remaining = 0 13 | self.percent_complete = 0 14 | self.active_send_threads = 0 15 | self.sent_total = 0 16 | self.sent_last_one_sec = 0 17 | self.sent_avg_per_sec = 0 18 | self.recv_success_total = 0 19 | self.recv_success_last_one_sec = 0 20 | self.recv_success_avg_per_sec = 0 21 | self.recv_total = 0 22 | self.recv_total_last_one_sec = 0 23 | self.recv_total_avg_per_sec = 0 24 | self.pcap_drop_total = 0 25 | self.drop_last_one_sec = 0 26 | self.drop_avg_per_sec = 0 27 | self.sendto_fail_total = 0 28 | self.sendto_fail_last_one_sec = 0 29 | self.sendto_fail_avg_per_sec = 0 30 | 31 | 32 | class ShellExecuteError(BaseException): 33 | def __init__(self, error_msg): 34 | super(ShellExecuteError, self).__init__(error_msg) 35 | 36 | 37 | def create_parent_dir(path): 38 | parent = os.path.dirname(path) 39 | if not os.path.exists(parent): 40 | os.makedirs(parent) 41 | 42 | 43 | def get_last_line(path): 44 | cmd = "tail -n 1 %s" % path 45 | p = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 46 | return_code = p.wait() 47 | if return_code == 0: 48 | return p.stdout.read().strip() 49 | else: 50 | raise ShellExecuteError(p.stderr.read()) 51 | 52 | 53 | def get_current_status(status_path): 54 | try: 55 | line = get_last_line(status_path) 56 | except ShellExecuteError: 57 | return None 58 | if line.startswith("real-time"): 59 | return None 60 | status = ZmapStatus() 61 | items = line.split(",") 62 | t = time.strptime(items[0], "%Y-%m-%d %X") 63 | y, m, d, h, M, s = t[0:6] 64 | status.read_time = datetime.datetime(y, m, d, h, M, s, tzinfo=timezone.LocalTimezone()) 65 | status.time_elapsed = int(items[1]) 66 | status.time_remaining = int(items[2]) 67 | status.percent_complete = float(items[3]) 68 | status.active_send_threads = int(items[4]) 69 | status.sent_total = long(items[5]) 70 | status.sent_last_one_sec = int(items[6]) 71 | status.sent_avg_per_sec = int(items[7]) 72 | status.recv_success_total = long(items[8]) 73 | status.recv_success_last_one_sec = int(items[9]) 74 | status.recv_success_avg_per_sec = int(items[10]) 75 | status.recv_total = long(items[11]) 76 | status.recv_total_last_one_sec = int(items[12]) 77 | status.recv_total_avg_per_sec = int(items[13]) 78 | status.pcap_drop_total = long(items[14]) 79 | status.drop_last_one_sec = int(items[15]) 80 | status.drop_avg_per_sec = int(items[16]) 81 | status.sendto_fail_total = long(items[17]) 82 | status.sendto_fail_last_one_sec = int(items[18]) 83 | status.sendto_fail_avg_per_sec = int(items[19]) 84 | return status 85 | 86 | 87 | class Zmap(object): 88 | def __init__(self, execute_bin='zmap', verbosity=3, cwd=None): 89 | self.execute_bin = execute_bin 90 | self.verbosity = verbosity 91 | self.cwd = cwd 92 | 93 | def run_job(self, job): 94 | pass 95 | 96 | def scan(self, port, subnets=None, output_path=None, log_path=None, bandwidth=2, white_list=None, black_list=None, 97 | verbosity=None, status_updates_path=None, quiet=False, stdout=None, stderr=None): 98 | if verbosity: 99 | self.verbosity = verbosity 100 | cmd = "%s -p %s" % (self.execute_bin, port) 101 | if output_path: 102 | output_path = os.path.join(self.cwd, output_path) 103 | create_parent_dir(output_path) 104 | cmd += ' -o %s' % output_path 105 | if bandwidth: 106 | cmd += " -B %sM" % bandwidth 107 | if white_list: 108 | white_list = os.path.join(self.cwd, white_list) 109 | create_parent_dir(white_list) 110 | cmd += " -w %s" % white_list 111 | if black_list: 112 | black_list = os.path.join(self.cwd, black_list) 113 | create_parent_dir(black_list) 114 | cmd += " -b %s" % black_list 115 | if status_updates_path: 116 | status_updates_path = os.path.join(self.cwd, status_updates_path) 117 | create_parent_dir(status_updates_path) 118 | cmd += " -u %s" % status_updates_path 119 | if log_path: 120 | log_path = os.path.join(self.cwd, log_path) 121 | create_parent_dir(log_path) 122 | cmd += " -l %s" % log_path 123 | cmd += ' -v %s' % self.verbosity 124 | if subnets: 125 | cmd += ' ' + subnets 126 | if quiet: 127 | cmd += ' -q' 128 | cmd = filter(lambda x: x.strip() != '', cmd.split(" ")) 129 | return subprocess.Popen(cmd, stderr=stderr, stdout=stdout, cwd=self.cwd) 130 | 131 | 132 | if __name__ == '__main__': 133 | zmap = Zmap() 134 | p = zmap.scan(port=80, output_path=None, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 135 | p.wait() 136 | -------------------------------------------------------------------------------- /webzmap/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengyouchao/webzmap/abc2f68109c482eea794664adc018a4beedd4bfd/webzmap/__init__.py -------------------------------------------------------------------------------- /webzmap/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for webzmap project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.9.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.9/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = '1!)_cazcc3$6^210!2xmcu$^r*j)%acmwdv!!0670o+osm!7w^' 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | # Application definition 30 | 31 | INSTALLED_APPS = [ 32 | 'django.contrib.admin', 33 | 'django.contrib.auth', 34 | 'django.contrib.contenttypes', 35 | 'django.contrib.sessions', 36 | 'django.contrib.messages', 37 | 'django.contrib.staticfiles', 38 | 'rest_framework', 39 | 'core', 40 | ] 41 | 42 | MIDDLEWARE_CLASSES = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'webzmap.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'webzmap.wsgi.application' 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.sqlite3', 79 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 80 | } 81 | } 82 | 83 | # Password validation 84 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators 85 | 86 | AUTH_PASSWORD_VALIDATORS = [ 87 | { 88 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 89 | }, 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 98 | }, 99 | ] 100 | 101 | # Internationalization 102 | # https://docs.djangoproject.com/en/1.9/topics/i18n/ 103 | 104 | LANGUAGE_CODE = 'zh-hans' 105 | 106 | TIME_ZONE = 'Asia/Shanghai' 107 | 108 | USE_I18N = True 109 | 110 | USE_L10N = True 111 | 112 | USE_TZ = True 113 | 114 | # Static files (CSS, JavaScript, Images) 115 | # https://docs.djangoproject.com/en/1.9/howto/static-files/ 116 | WORK_DIR = os.path.join(BASE_DIR, "workspace") 117 | 118 | STATIC_URL = '/static/' 119 | 120 | STATICFILES_DIRS = ( 121 | os.path.join(BASE_DIR, "static"), 122 | os.path.join(WORK_DIR), 123 | ) 124 | 125 | REST_FRAMEWORK = { 126 | # Use Django's standard `django.contrib.auth` permissions, 127 | # or allow read-only access for unauthenticated users. 128 | 'DEFAULT_PERMISSION_CLASSES': [ 129 | 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' 130 | ] 131 | } 132 | 133 | if not os.path.exists(WORK_DIR): 134 | os.makedirs(WORK_DIR) 135 | os.chmod(WORK_DIR, 0777) 136 | 137 | ZMAP_CWD = WORK_DIR 138 | 139 | LOGGING = { 140 | 'version': 1, 141 | 'disable_existing_loggers': True, 142 | 'formatters': { 143 | 'standard': { 144 | 'format': '%(levelname)s %(asctime)s %(message)s' 145 | }, 146 | }, 147 | 'filters': { 148 | }, 149 | 'handlers': { 150 | 'mail_admins': { 151 | 'level': 'ERROR', 152 | 'class': 'django.utils.log.AdminEmailHandler', 153 | 'formatter': 'standard', 154 | }, 155 | 'file_handler': { 156 | 'level': 'DEBUG', 157 | 'class': 'logging.handlers.RotatingFileHandler', 158 | 'filename': os.path.join(BASE_DIR, 'zmapd.log'), 159 | 'formatter': 'standard', 160 | }, 161 | }, 162 | 'loggers': { 163 | 'django.request': { 164 | 'handlers': ['mail_admins'], 165 | 'level': 'ERROR', 166 | 'propagate': True, 167 | }, 168 | 'zmapd': { 169 | 'handlers': ['file_handler'], 170 | 'level': 'DEBUG', 171 | 'propagate': False 172 | }, 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /webzmap/urls.py: -------------------------------------------------------------------------------- 1 | """webzmap URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.9/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url, include 17 | from django.contrib import admin 18 | 19 | from core.models import Job, WhiteListFile, BlackListFile, Command 20 | from rest_framework import routers, serializers, viewsets 21 | 22 | 23 | # Serializers define the API representation. 24 | class JobSerializer(serializers.HyperlinkedModelSerializer): 25 | class Meta: 26 | model = Job 27 | fields = ( 28 | 'url', 'id', 'name', 'port', 'subnets', 'status', 'bandwidth', 'verbosity', 'priority', 'creation_time', 29 | 'pid', 30 | 'read_time', 31 | 'time_elapsed', 32 | 'time_remaining', 'percent_complete', 'active_send_threads', 'sent_total', 'sent_last_one_sec', 33 | 'sent_avg_per_sec', 'recv_success_total', 'recv_success_last_one_sec', 'recv_success_avg_per_sec', 34 | 'recv_total', 35 | 'recv_total_last_one_sec', 'recv_total_avg_per_sec', 'pcap_drop_total', 'drop_last_one_sec', 36 | 'drop_avg_per_sec', 37 | 'sendto_fail_total', 'sendto_fail_last_one_sec', 'sendto_fail_avg_per_sec', 'white_list_file', 38 | 'black_list_file') 39 | # fields = '__all__' 40 | # exclude = ('output_path', 'log_path', 'status_path') 41 | read_only_fields = ( 42 | 'id', 'status', 'creation_time', 'pid', 'read_time', 'time_elapsed', 'time_remaining', 'percent_complete', 43 | 'active_send_threads', 'sent_total', 'sent_last_one_sec', 'sent_avg_per_sec', 44 | 'recv_success_total', 45 | 'recv_success_last_one_sec', 'recv_success_avg_per_sec', 'recv_total', 46 | 'recv_total_last_one_sec', 47 | 'recv_total_avg_per_sec', 'pcap_drop_total', 'drop_last_one_sec', 'drop_avg_per_sec', 48 | 'sendto_fail_total', 'sendto_fail_last_one_sec', 'sendto_fail_avg_per_sec') 49 | 50 | 51 | class WhiteListFileSerializer(serializers.HyperlinkedModelSerializer): 52 | class Meta: 53 | model = WhiteListFile 54 | fields = "__all__" 55 | read_only_fields = ('size',) 56 | extra_kwargs = {'file': {'write_only': True}} 57 | 58 | 59 | class BlackListFileSerializer(serializers.HyperlinkedModelSerializer): 60 | class Meta: 61 | model = BlackListFile 62 | fields = "__all__" 63 | read_only_fields = ('size',) 64 | extra_kwargs = {'file': {'write_only': True}} 65 | 66 | 67 | class CommandSerializer(serializers.HyperlinkedModelSerializer): 68 | class Meta: 69 | model = Command 70 | fields = "__all__" 71 | read_only_fields = ('creation_time',) 72 | 73 | 74 | # ViewSets define the view behavior. 75 | class JobViewSet(viewsets.ModelViewSet): 76 | queryset = Job.objects.all().order_by('-creation_time') 77 | serializer_class = JobSerializer 78 | 79 | 80 | class WhiteListFileViewSet(viewsets.ModelViewSet): 81 | queryset = WhiteListFile.objects.all() 82 | serializer_class = WhiteListFileSerializer 83 | 84 | 85 | class BlackListFileViewSet(viewsets.ModelViewSet): 86 | queryset = BlackListFile.objects.all() 87 | serializer_class = BlackListFileSerializer 88 | 89 | 90 | class CommandViewSet(viewsets.ModelViewSet): 91 | queryset = Command.objects.all() 92 | serializer_class = CommandSerializer 93 | 94 | 95 | # Routers provide an easy way of automatically determining the URL conf. 96 | router = routers.DefaultRouter() 97 | router.register(r'jobs', JobViewSet) 98 | router.register(r'whitelistfiles', WhiteListFileViewSet) 99 | router.register(r'blacklistfiles', BlackListFileViewSet) 100 | router.register(r'commands', CommandViewSet) 101 | 102 | urlpatterns = [ 103 | url(r'^', include("core.urls", namespace="core")), 104 | url(r'^admin/', include(admin.site.urls)), 105 | url(r'^api/', include(router.urls)), 106 | url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) 107 | ] 108 | -------------------------------------------------------------------------------- /webzmap/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for webzmap project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webzmap.settings") 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------