Monday, April 30, 2012

Django as a micro framework, including models

The following can be used as a base when you want to avoid the normal structure Django imposes on your project, and you just want everything in a single file.

It is based on similar attempts by Ivan Sagalaev and Fahrzin Hemmati (especially this gist), and a post on stack overflow.

A thing that I consider an improvement over Fahrzin Hemmati's version is that models don't need the explicit app_label and __module__.

#
# Django as a microframework skeleton, by Allan Boll, 1 May 2012
# Based on http://softwaremaniacs.org/blog/2011/01/07/django-micro-framework/en/
# and https://gist.github.com/2219751
#

#
# Settings
#
appname = 'app'
from django.conf import settings
if not settings.configured:
    settings.configure(
        SITE_ID=1,
        TEMPLATE_DIRS=['.'],
        DATABASES = {
            'default':{
                'ENGINE':'django.db.backends.sqlite3',
                'NAME': 'db',
            }
        },
        DEBUG=True,
        TEMPLATE_DEBUG=True,
        ROOT_URLCONF = __name__,
        STATIC_URL='/static/',
        STATICFILES_DIRS=['./static/'],
        INSTALLED_APPS=(
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'django.contrib.admin', 
        )
    )

#
# Models
#
from django.db import models

# Workaround to allow models in current file
import sys
sys.modules[appname+'.'] = sys.modules[__name__]
old_module_name = sys.modules[__name__].__name__
sys.modules[__name__].__name__ = appname+'.'

class SomeModel(models.Model):
    field_name = models.CharField(max_length=10)

# Continuation of workaround to allow models in current file
sys.modules[__name__].__name__ = old_module_name


#
# Views
#
from django.shortcuts import render

def index(request):
    out = []
    for obj in SomeModel.objects.all():
        out.append(obj.field_name)
    return render(request, 'index.html', {'s': ', '.join(out)})


#
# URL configuration
#
from django.conf.urls.defaults import patterns, url, include

urlpatterns = patterns('',
    (r'^$', index),
)


#
# Admin site
#
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.contrib.auth.models import User, Group

admin.site = admin.AdminSite()
admin.site.register(User, UserAdmin)
admin.site.register(Group, GroupAdmin)
admin.site.register(SomeModel)

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls))
) + urlpatterns


#
# Running
#
if __name__=='__main__':
    # Monkey patch get_app to allow models in current file
    get_app_orig = models.get_app
    def get_app(app_label,*a, **kw):
        if app_label==appname:
            return sys.modules[__name__]
        return get_app_orig(app_label, *a, **kw)
    models.get_app = get_app

    # Add models in current file to global list of apps
    models.loading.cache.app_store[type(appname+'.models',(),{'__file__':__file__})] = appname

    from django.core import management
    management.execute_from_command_line()

Friday, February 10, 2012

Sublime Text default to empty file

I recently began using the editor Sublime Text. Previously I was using Notepad++ which is also a fine editor, but not as "futuristic" as Sublime Text. I had gotten used to Notepad++'s behaviour of defaulting to an empty file when all tabs are closed, and found that this behaviour was easy to achieve in Sublime Text as well, thanks to its amazing API. To get the behaviour, drop the following Python source into something like Data/Packages/User/default_to_empty_file.py.


import sublime, sublime_plugin

class DefaultToEmptyFile(sublime_plugin.EventListener):
def on_close(self, view):
for window in sublime.windows():
if window == None:
continue

if len(window.views()) <= 1:
window.new_file()

def on_load(self, view):
window = view.window()
if window == None:
return

views = window.views()
if len(views) == 2:
for v in views:
if v != view and v.file_name() == None and v.size() == 0:
window.focus_view(v)
window.run_command('close_file')

Friday, December 30, 2011

Raw sockets with BPF in Python

Update 2021: Note that this was written in 2011. Nowadays I'd not recommend doing it this way, but instead using BCC to write your filters in C, from within Python. The following example shows the use of raw sockets where filtering is applied using BPF. It has only been tested on Linux. The filter data structure is built up to form a machine language-like set of instructions that decide whether the packet should be passed to the raw socket or not. The filter is applied to the socket using SO_ATTACH_FILTER. This specific example filters packets to only allow packets destined a DHCP server (UDP port 67).
from binascii import hexlify
from ctypes import create_string_buffer, addressof
from socket import socket, AF_PACKET, SOCK_RAW, SOL_SOCKET
from struct import pack, unpack


# A subset of Berkeley Packet Filter constants and macros, as defined in
# linux/filter.h.

# Instruction classes
BPF_LD = 0x00
BPF_JMP = 0x05
BPF_RET = 0x06

# ld/ldx fields
BPF_H = 0x08
BPF_B = 0x10
BPF_ABS = 0x20

# alu/jmp fields
BPF_JEQ = 0x10
BPF_K = 0x00

def bpf_jump(code, k, jt, jf):
    return pack('HBBI', code, jt, jf, k)

def bpf_stmt(code, k):
    return bpf_jump(code, k, 0, 0)


# Ordering of the filters is backwards of what would be intuitive for 
# performance reasons: the check that is most likely to fail is first.
filters_list = [
    # Must have dst port 67. Load (BPF_LD) a half word value (BPF_H) in 
    # ethernet frame at absolute byte offset 36 (BPF_ABS). If value is equal to
    # 67 then do not jump, else jump 5 statements.
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 36),
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 67, 0, 5),

    # Must be UDP (check protocol field at byte offset 23)
    bpf_stmt(BPF_LD | BPF_B | BPF_ABS, 23), 
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 3),

    # Must be IPv4 (check ethertype field at byte offset 12)
    bpf_stmt(BPF_LD | BPF_H | BPF_ABS, 12), 
    bpf_jump(BPF_JMP | BPF_JEQ | BPF_K, 0x0800, 0, 1),

    bpf_stmt(BPF_RET | BPF_K, 0x0fffffff), # pass
    bpf_stmt(BPF_RET | BPF_K, 0), # reject
]

# Create filters struct and fprog struct to be used by SO_ATTACH_FILTER, as
# defined in linux/filter.h.
filters = ''.join(filters_list)
b = create_string_buffer(filters)
mem_addr_of_filters = addressof(b)
fprog = pack('HL', len(filters_list), mem_addr_of_filters)

# As defined in asm/socket.h
SO_ATTACH_FILTER = 26

# Create listening socket with filters
s = socket(AF_PACKET, SOCK_RAW, 0x0800)
s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, fprog)
s.bind(('eth0', 0x0800))

while True:
    data, addr = s.recvfrom(65565)
    print 'got data from', addr, ':', hexlify(data)

Thursday, December 08, 2011

Simple heap implementation in C#

I recently needed a simple priority queue in C# and was surprised to find that there is neither anything called "priority queue" nor "min heap" or "max heap" in .NET (or at least I did not find it when I looked). Here is a textbook-like implementation I came up with:

class MinHeap<T> where T : IComparable
{
    private List<T> data = new List<T>();

    public void Insert(T o)
    {
        data.Add(o);

        int i = data.Count - 1;
        while (i > 0)
        {
            int j = (i + 1) / 2 - 1;

            // Check if the invariant holds for the element in data[i]
            T v = data[j];
            if (v.CompareTo(data[i]) < 0 || v.CompareTo(data[i]) == 0)
            {
                break;
            }

            // Swap the elements
            T tmp = data[i];
            data[i] = data[j];
            data[j] = tmp;

            i = j;
        }
    }

    public T ExtractMin()
    {
        if (data.Count < 0)
        {
            throw new ArgumentOutOfRangeException();
        }

        T min = data[0];
        data[0] = data[data.Count - 1];
        data.RemoveAt(data.Count - 1);
        this.MinHeapify(0);
        return min;
    }

    public T Peek()
    {
        return data[0];
    }

    public int Count
    {
        get { return data.Count; }
    }

    private void MinHeapify(int i)
    {
        int smallest;
        int l = 2 * (i + 1) - 1;
        int r = 2 * (i + 1) - 1 + 1;

        if (l < data.Count && (data[l].CompareTo(data[i]) < 0))
        {
            smallest = l;
        }
        else
        {
            smallest = i;
        }

        if (r < data.Count && (data[r].CompareTo(data[smallest]) < 0))
        {
            smallest = r;
        }

        if (smallest != i)
        {
            T tmp = data[i];
            data[i] = data[smallest];
            data[smallest] = tmp;
            this.MinHeapify(smallest);
        }
    }
}

Sunday, February 28, 2010

Automatic archiving of IMAP mailbox

I was looking for a way to let my IMAP server automatically move old mail to the correct IMAP folder based on a given specification. I wanted to simply drag mail that I wanted archived to a IMAP folder called Archives, and then let the server put it in the correct subfolder itself. Inspired by http://wiki.dovecot.org/HowTo/RefilterMail , I wrote this Python script:

#!/usr/bin/python
import imaplib, re, os

server = imaplib.IMAP4('localhost')
server.login('username', 'password')
r = server.select('Archives')
resp, items = server.search(None, 'ALL')

for m in items[0].split():
resp, data = server.fetch(m, '(BODY[HEADER.FIELDS (SUBJECT FROM TO)])')
_, headers = data[0]

dst = None
if re.search('To: .*@student.dtu.dk', headers): dst = 'Archives.2010.DTU'
if re.search('Subject: .*\[somelist\]', headers): dst = 'Archives.2010.Somelist'
if dst == None: dst = 'Archives.2010'

resp, data = server.copy(m, dst)
if resp == 'OK':
server.store(m, '+Flags', '\\Deleted')

server.expunge()
You can obscure the password by using base64.b64decode to prevent people seeing you password when looking over your shoulder.

To make the script run automatically every time a mail is dropped to the Archives folder, use incron. Type incrontab -e and write the following line
/home/username/Maildir/.Archives/cur/ IN_MOVED_TO,IN_ONESHOT python /path/to/script
Where /path/to/script here is the path of the above Python script.

Also, if you want to use this automatic triggering, add the following line to the end of the Python script
os.system('incrontab --reload')

Friday, January 22, 2010

Rogue DHCP detector in Nagios

This Python script I created can be used with Nagios to warn if there are unauthorized DHCP servers on the network. No third party libraries are used.

At the moment the script only checks at IP level, and does not return the MAC address of the rogue server. My plan is to expand this at some stage, but at the moment we do not really need this in our setup.


#!/usr/bin/python
from socket import *
from binascii import *
from random import *
from struct import *

# local machine mac address
chaddr = unhexlify('001122334455')

# allowed dhcp servers
whitelist = set(['11.22.33.44'])

# random session id (xid)
xid = pack('L', randrange(0, 2**32 - 1 ))

# setup socket
s = socket(AF_INET, SOCK_DGRAM)
s.bind(('0.0.0.0', 68))
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)

request = \
'\x01\x01\x06\x00' \
+ xid \
+ ''.ljust(20, '\x00') \
+ chaddr.ljust(16, '\x00') \
+ ''.ljust(192, '\x00') \
+ '\x63\x82\x53\x63' \
+ '\x35\x01\x03' \
+ '\xff'
s.sendto(request, ('255.255.255.255', 67))

# listen for dhcp packets for max 2.5 seconds
status = "OK - No rogue dhcp servers detected"
r = 0
s.settimeout(2.5)
while 1:
try:
buf, (ip, port) = s.recvfrom(65565)
except:
break
opcode, = unpack_from('B', buf)
if not (ip in whitelist and opcode == 0x02):
r = 2
status = "CRITICAL - Rogue dhcp server detected on IP-addr: " + ip
break

s.close()
print status
exit(r)


Monday, December 21, 2009

Thunderbird 3: awful search

The new search feature in Thunderbird 3 is horrible! It is hard to get an overview on this new layout, which can only contain a very limited amount of results. The idea of caching messages in a local database for faster search is good, but quite late... I would have prefered the classic search result view (which is what you expect from a mail program), but with the new cached backend.

Update: just noticed that it is possible to click the little magnifying glass in the input field and choose "Subject, From, or Recipient filter" to get the old style search back!

Update again: looks like the the horrible search has been dropped in version 3.1 :-)