1/2にPerlで作ったSIP ProxyをPythonに移植してみた

ついでにRecord-Routeをいれてみたり、いくつかのヘッダチェック処理も追加してみた。
あと、起動時にユーザの情報が書かれたCSVファイルを読み込むようにした。
CSVファイルは1カラム目がuser名、2カラム目が認証用パスワード、3カラム目がコンタクトアドレス。
2カラム目以降はなくともよい。
2カラム目が未設定か空文字の場合は認証しない。
3カラム目が書いてある場合はREGISTERしていなくてもリクエストを受信できる。


起動は、
このスクリプト ドメイン IPアドレス ポート番号 上記CSVファイル
って感じ。


IPv6未対応、TCP未対応、maddr未対応、など制限あり。
動かすと汚いログが標準出力にいっぱい出ます。ごめんなさい。気が向いたらこっそり直します。


ところで移植作業をしていてふと思ったのだけれど、あるロジックを考えたとき、複数の言語で容易に実装できるロジックは優れていることが多いんじゃないかと思った。特定の言語に依存しているロジックである場合、それはロジックそのものに難しさが潜んでいて、それを言語が吸収してくれているだけじゃないかと思う。

つまり、あるロジックがイケてるか悩んだら、複数の言語で容易に実装できるかを想像するといいのかもしれない。


以降ソース。いずれ組み込もうと思っている機能のソースが混ざっています。ご容赦ください。


※3月頭追記。SourceforgeにてRubyでそこそこまともなProxyを作る作業ばかりしているのでこのプログラムはたぶんもう修正しません。。

#!/usr/bin/python -O
# encoding: utf-8

"""

    stateless sip proxy server

"""


import sys, re, socket, md5, csv, Queue

#############################################################################
class NotFound(Exception):

    # -----------------------------------------------------------------------
    def __init__(self, s):
        self.val = s

    # -----------------------------------------------------------------------
    def __str__(self):
        return self.val

#############################################################################
class SentResponse(Exception):

    # -----------------------------------------------------------------------
    def __init__(self, s):
        self.val = s

    # -----------------------------------------------------------------------
    def __str__(self):
        return self.val

#############################################################################
class Parser:

    # -----------------------------------------------------------------------
    def parse_addr_spec(self, buf):
        if __debug__:
            print 'Parser.parse_addr_spec(', buf, ')'
        m = re.match(r'sip:(([^@]+)@)?([^;:]+)(:(\d+))?', buf)
        if m.group(2) != None:
            user = m.group(2)
        else:
            user = ''
        if m.group(5) != None:
            port = int(m.group(5))
        else:
            port = 5060
        return user, m.group(3), port

    # -----------------------------------------------------------------------
    def parse_name_addr(self, buf):
        if __debug__:
            print 'Parser.parse_name_addr(', buf, ')'
        m = re.search(r'<([^>]+)>', buf)
        if m != None:
            return buf[:m.start()], m.group(1), buf[m.end():]
        m = re.search(r'([^@]+@)?[^;]+', buf)
        return "", buf[:m.end()], buf[m.end():]


#############################################################################
class SIPHeader:

    # -----------------------------------------------------------------------
    def __init__(self, name = '', vals = []):
        self.name = name
        self.vals = vals

    # -----------------------------------------------------------------------
    def __str__(self):
        return ''.join([self.name, ': ', ', '.join(self.vals), "\r\n"])


#############################################################################
class SIPMessage(Parser):

    # -----------------------------------------------------------------------
    def __init__(self, buf=None):
        self.method = self.requri = self.stcode = self.reason = ''
        self.hdrs = []
        self.body = ""
        if buf != None:
            self.parse(buf)

    # -----------------------------------------------------------------------
    def parse(self, buf):
        buf, self.body = self.__parse_body(buf.lstrip())
        buf = re.sub(r'\r\n?', '\n', buf)
        buf = re.sub(r"\n[ \t]", " ", buf)
        ary = re.split(r'\n', buf)
        self.method, self.requri, self.stcode, self.reason = \
            self.__parse_start_line(ary[0])
        self.hdrs = self.__parse_headers(ary[1:])

    # -----------------------------------------------------------------------
    def __parse_start_line(self, buf):
        if __debug__:
            print 'SIPMessage.parse_start_line(', buf, ')'
        m = re.match(r'([^ ]+) ([^ ]+) ', buf)
        if m.group(1) == 'SIP/2.0':
            return '', '', m.group(2), buf[m.end():]
        return m.group(1), m.group(2), '', ''

    # -----------------------------------------------------------------------
    def __parse_headers(self, ary):
        if __debug__:
            print 'SIPMessage.parse_header(...)'
        hdrs = []
        for hdr in ary:
            hdrs.append(self.__parse_header(hdr))
        return hdrs

    # -----------------------------------------------------------------------
    def __parse_header(self, buf):
        if __debug__:
            print 'SIPMessage.parse_header(', buf, ')'
        hname, buf = re.split(r'\s*:\s*', buf, 1)
        hvals = []
        while buf != '':
            m = re.match(r'(([^,"]|"([^\\]|(\\"))*?")+)(\s*,\s*)?', buf)
            hvals.append(m.group(1))
            buf = buf[m.end():]
        return SIPHeader(hname, hvals)

    # -----------------------------------------------------------------------
    def __parse_body(self, buf):
        if __debug__:
            print 'SIPMessage.__parse_body(...)'
        m = re.search(r'(\r\n\r\n)|(\r\r)|(\n\n)', buf)
        return buf[:m.start()], buf[m.end():]

    # -----------------------------------------------------------------------
    def __str__(self):
        if self.method != '':
            s = self.method + ' ' + self.requri + ' SIP/2.0\r\n'
        else:
            s = 'SIP/2.0 ' + self.stcode + " " + self.reason + '\r\n'
        for h in self.hdrs:
            s += str(h)
        s += '\r\n' + self.body
        return s

    # -----------------------------------------------------------------------
    def insert_received(self, addr):
        if __debug__:
            print 'SIPMessage.insert_received(', addr[0], ')'
        pos = self.search('via', 'v')
        topvia = self.hdrs[pos].vals[0]
        topvia += ';received=' + str(addr[0])
        m = re.search(r';rport', topvia)
        if m != None:
            topvia = topvia[:m.end()] + '=' + str(addr[1]) + topvia[m.end():] 
        self.hdrs[pos].vals[0] = topvia

    # -----------------------------------------------------------------------
    def search(self, name1, name2 = ''):
        if __debug__:
            print 'SIPMessage.search(', name1, ', ', name2, ')'
        for i, hdr in enumerate(self.hdrs):
            if hdr.name.lower() == name1 or hdr.name.lower() == name2:
                return i
        raise NotFound, 'header ' + name1 + ' ' + name2 + ' not found'

    # -----------------------------------------------------------------------
    def rsearch(self, name1, name2 = ''):
        if __debug__:
            print 'SIPMessage.rsearch(', name1, ', ', name2, ')'
        i = 0
        pos = -1
        for hdr in slef.hdrs:
            if hdr.name.lower() == name1 or hdr.name.lower() == name2:
                pos = i
            i += 1
        if pos == -1:
            raise NotFound, 'header ' + name1 + ' ' + name2 + ' not found'
        return pos

    # -----------------------------------------------------------------------
    def search_addr_spec(self, name1, name2):
        if __debug__:
            print 'SIPMessage.search_addr_spec(', name1, ', ', name2, ')'
        tmp0, addr_spec, tmp1 = self.search_name_addr(name1, name2)
        return self.parse_addr_spec(addr_spec)

    # -----------------------------------------------------------------------
    def search_name_addr(self, name1, name2):
        if __debug__:
            print 'SIPMessage.search_name_addr(', name1, ', ', name2, ')'
        pos = self.search(name1, name2)
        return self.parse_name_addr(self.hdrs[pos].vals[0])

    # -----------------------------------------------------------------------
    def delete_last(self, name1, name2 = ""):
        if __debug__:
            print 'SIPMessage.delete_last(', name1, ', ', name2, ')'
        hpos = self.rsearch(name1, name2)
        vpos = len(self.hdrs[hpos].vals) - 1
        ret = self.hdrs[hpos].vals[vpos]
        del(self.hdrs[hpos].vals[vpos])
        if len(self.hdrs[hpos].vals) == 0:
            del(self.hdrs[hpos])
        return ret

    # -----------------------------------------------------------------------
    def conv2resp(self, stcode, reason):
        if __debug__:
            print 'SIPMessage.conv2resp(', stcode, ', ', reason, ')'
        hnames = ['call-id', 'i', 'from', 'f', 'to', 't', 'via', 'v',
                  'cseq', 'max-forwards']
        self.method = self.requri = self.body = ''
        self.stcode = stcode
        self.reason = reason
        new_hdrs = []
        for hdr in self.hdrs:
            if hdr.name.lower() in hnames:
                new_hdrs.append(hdr)
        self.hdrs = new_hdrs

    # -----------------------------------------------------------------------
    def get_dest(self):
        if __debug__:
            print 'SIPMessage.get_dest()'
        if self.method != '':
            return self.get_req_dest()
        return self.get_resp_dest()

    # -----------------------------------------------------------------------
    def get_req_dest(self):
        if __debug__:
            print 'SIPMessage.get_req_dest()'
        user, host, port = self.parse_addr_spec(self.requri)
        return host, port

    # -----------------------------------------------------------------------
    def get_resp_dest(self):
        if __debug__:
            print 'SIPMessage.get_resp_dest()'
        v = self.hdrs[self.search("via", "v")].vals[0]
        m = re.match(r'SIP\s*/\s*2\.0\s*/UDP\s+', v)
        v = v[m.end():]
        m = re.match(r'([^:;]+)(\s*:\s*(\d+))?', v)
        host = m.group(1)
        port = m.group(3)
        if port == None:
            port = 5060
        m = re.search(r';received=([^;]+)', v)
        if m != None:
            host = m.group(1)
        m = re.search(r';rport=([\d]+)', v)
        if m != None:
            port = m.group(1)
        return host, int(port)

#############################################################################
class Timer:

    # -----------------------------------------------------------------------
    def __init__(self, type, interval, timeout, src):
        self.type = type
        self.interval = interval
        self.timeout = timeout
        self.dest = src
        self.src = "TIMER"

#############################################################################
class TimerManager:

    # -----------------------------------------------------------------------
    def __init__(self):
        self.list_interval = [ 0.5,  1.5,  3.5,  5.0,  7.5, 11.5,
                              15.5, 19.5, 23.5, 27.5, 31.5, 32.0, None]
        self.lists = []
        for interval in self.list_interval:
            self.lists.append(Queue.Queue())

    # -----------------------------------------------------------------------
    def set_timer(self, type, interval, src):
        t = Timer(type, interval, interval + time.time(), src)

#############################################################################
class SIPTransportLayer:

    # -----------------------------------------------------------------------
    def __init__(self):
        pass

#############################################################################
class SIPTransaction:

    # -----------------------------------------------------------------------
    def __init__(self):
        pass

    # -----------------------------------------------------------------------
    def sendmsg(self, msg, retransmit = [], timeout = None):
        if msg.method != '':
            if msg.method == 'INVITE':
                pass
            else:
                pass
        else:
            # get cseq value
            if cseq_method == 'INVITE':
                pass
            else:
                pass
    # -----------------------------------------------------------------------
    def proc_ict_none(self, msg):
        if __debug__:
            print 'RFC3261 17.1.1 INVITE Clinet Transaction'
        self.set_timer_ab()
        self.sendmsg(msg)
        self.set_state('ICTCalling')

    # -----------------------------------------------------------------------
    def proc_ict_calling(self, evt):
        self.proc_ict_proceeding(evt)

    # -----------------------------------------------------------------------
    def proc_ict_proceeding(self, evt):
        if isnstance(evt, SIPMessage):
            if evt.stcode[:1] == '1':
                self.set_state('ICTProceeding')
                self.pass_to_tu(evt)
            elif evt.stcode != '':
                ack = evt.gen_ack()
                self.sendmsg(ack)
                self.set_state('ICTCompleted')
                self.set_timer('TimerD', 32, time.time() + 32)
                self.pass_to_tu(evt)
        elif isinstance(evt, SIPTimer) and self.state == 'ICTCalling':
            if evt.type == 'TimerA':
                self.sendbuf()
            elif evt.type == 'TimerB':
                self.set_state('')

    # -----------------------------------------------------------------------
    def proc_ict_completed(self, evt):
        pass

    # -----------------------------------------------------------------------
    def proc_nict_none(self, msg):
        if __debug__:
            print 'RFC3261 17.1.2 non-INVITE Clinet Transaction'
        self.set_timer_ef()
        self.sendmsg(msg)
        self.set_state('NICTTrying')

    # -----------------------------------------------------------------------
    def proc_nict_trying(self, evt):
        self.proc_nict_proceeding(evt)

    # -----------------------------------------------------------------------
    def proc_nict_proceeding(self, evt):
        if isinstance(evt, SIPMessage):
            if evt.stcode[:1] == '1':
                self.set_state('NICTProceeding')
                self.pass_to_tu(evt)
            elif evt.stcode != '':
                self.set_timer('TimerK', T4, time.time() + T4)
                self.set_state('NICTCompleted')
                self.pass_to_tu(evt)
        elif isinstance(evt, SIPTimer):
            if evt.type == 'TimerE':
                self.sendbuf()
            elif evt.type == 'TimerF':
                self.set_state('')
                self.pass_to_tu(evt)

    # -----------------------------------------------------------------------
    def proc_nict_completed(self, evt):
        if isinstance(evt, SIPTimer):
            if evt.type == 'TimerK':
                self.set_state('')

    # -----------------------------------------------------------------------
    def proc_ist_none(self, msg):
        if __debug__:
            print 'RFC3261 17.2.1 INVITE Server Transaction'
        resp = msg.gen_resp('100', 'Trying')
        self.sendmsg(resp)
        self.set_state('ISTProceeding')
        self.pass_to_tu(msg)

    # -----------------------------------------------------------------------
    def proc_ist_proceeding(self, evt):
        if isinstance(evt, SIPMessage):
            if evt.method == 'INVITE':
                self.sendbuf(self.buf, self.destaddr)
            elif evt.stcode[:1] == '1':
                self.sendbuf(self.buf, self.destaddr)
            elif evt.stcode[:1] == '2':
                self.set_state(None)
            elif evt.stcode != '':
                self.set_state('ISTCompleted')
                self.set_timer_gh()
                self.send(evt)

    # -----------------------------------------------------------------------
    def proc_ist_completed(self, evt):
        if isinstance(evt, SIPMessage):
            if evt.method == 'INVITE':
                self.sendbug(self.buf, self.destaddr)
            elif evt.method == 'ACK':
                self.sendmsg(evt, T4)
                self.set_state('ISTCompleted')
        elif isinstance(evt, SIPTimer):
            if evt.type == 'TimerG':
                self.retransmit()
            elif evt.type == 'TimerH':
                self.set_state('')
                self.inform_trpt_err_to_tu(evt)

    # -----------------------------------------------------------------------
    def proc_ist_confirmed(self, evt):
        if isinstance(evt, SIPTimer):
            if evt.type == 'TimerI':
                self.set_state('')

    # -----------------------------------------------------------------------
    def proc_nist_none(self, msg):
        if __debug__:
            print 'RFC3261 17.2.2 Non-INVITE Server Transaction'
        self.set_state('NISTTrying')
        self.pass_to_tu(msg)

    # -----------------------------------------------------------------------
    def proc_nist_trying(self, evt):
        if isinstance(evt, SIPMessage):
            if evt.stcode[:1] == '1':
                self.set_state('NISTProceeding')
                self.sendmsg(evt)
            elif evt.stcode != '':
                self.set_state('NISTCompleted')
                self.set_timer('TimerJ')
                self.sendmsg(evt)

    # -----------------------------------------------------------------------
    def proc_nist_proceeding(self, evt):
        if isinstance(evt, SIPMessage):
            if evt.stcode[:1] == '1':
                self.sendmsg(evt)
            elif evt.stcode != '': # 200-299
                self.set_state('NISTCompleted')
                self.set_timer('TimerJ')
                self.sendmsg(evt)
            elif evt.method != '':
                self.sendbuf()

    # -----------------------------------------------------------------------
    def proc_nist_completed(self, evt):
        if isinstance(evt, SIPTimer):
            if evt.type == 'TimerJ':
                self.set_state('')

#############################################################################
class SIPTransactionManager:

    # -----------------------------------------------------------------------
    def __init__(self):
        self.data = {}

    # -----------------------------------------------------------------------
    def proc_evt(self, evt):
        id = evt.get_trx_id()
        if id in self.data:
            trx = self.data[id]
        else:
            trx = None
            if isinstance(evt, SIPMessage):
                if evt.method == 'ACK' or evt.method == '':
                    if evt.src == 'TRANSPORT':
                        self.pass_to_tu(evt)
                    elif evt.src == 'TU':
                        self.sendmsg(evt)
                else:
                    trx = SIPTransaction()
                    self.data[id] = trx
            else:
                if __debug__:
                    print 'transaction already terminated, ignore event'
        if trx != None:
            trx.proc_evt(evt)
            if trx.get_state() == '':
                del self.date[id]

#############################################################################
class UserData:

    # -----------------------------------------------------------------------
    def __init__(self, data):
        self.name = data[0]
        self.passwd = self.aor = ''
        if len(data) > 1:
            self.passwd = data[1]
        if len(data) > 2:
            self.contacts = [data[2]]

    # -----------------------------------------------------------------------
    def __str__(self):
        return ' '.join([self.name, self.passwd, ' '.join([self.contacts])])


#############################################################################
class Authenticate:

    # -----------------------------------------------------------------------
    def authenticate(self, hdr, method, password, body = ''):
        if __debug__:
            print 'Authenticate.authenticate(...)'
        prm = {}
        prm['algorithm'] = 'md5'
        prm['username'] = prm['nonce'] = prm['nc'] = prm['cnonce'] = ''
        prm['realm'] = prm['uri'] = prm['qop'] = ''
        prm['method'] = method
        prm['passwd'] = password
        prm['body'] = body
        m = re.match(
            r'Auth((enticate)|(orization))\s*:\s*Digest\s+', hdr)
        buf = hdr[m.end():]
        while buf != '':
            m = re.match(r'([a-z]+)="?([^"\s,]+)"?\s*(,\s*)?', buf)
            prm[m.group(1)] = m.group(2)
            buf = buf[m.end():]
        return (prm['response'] == self.calc_request_digest(prm))

    # -----------------------------------------------------------------------
    def calc_request_digest(self, prm):
        if __debug__:
            print 'Authenticate.calc_request_digest(...)'
        ary = [self.h(self.a1(prm)), prm['nonce']]
        if prm.has_key('qop'):
            ary.append(prm['nc'])
            ary.append(prm['cnonce'])
            ary.append(prm['qop'])
        ary.append(self.h(self.a2(prm)))
        ret = self.h(':'.join(ary))
        return ret

    # -----------------------------------------------------------------------
    def a1(self, prm):
        if __debug__:
            print 'Authenticate.a1(...)'
        ret = ':'.join([prm['username'], prm['realm'], prm['passwd']])
        if prm['algorithm'].lower() == 'md5-sess':
            ret = self.h(':'.join([ret, prm['nonce'], prm['cnonce']]))
        return ret

    # -----------------------------------------------------------------------
    def a2(self, prm, body = ''):
        if __debug__:
            print 'Authenticate.a2(...)'
        if prm['qop'] == 'auth-int':
            ret = ':'.join([prm['method'], prm['uri'], self.h(body)])
        else:
            ret = ':'.join([prm['method'], prm['uri']])
        return ret

    # -----------------------------------------------------------------------
    def h(self, data):
        if __debug__:
            print 'Authenticate.h(...)'
        return md5.new(data).hexdigest()

#############################################################################
class Proxy(Authenticate, Parser):

    # -----------------------------------------------------------------------
    def initialize(self, domain, host, port, user_data):
        if __debug__:
            print 'Proxy.initialize(', str(host), ', ', str(port), ')'
        self.locdata = {}
        self.load(user_data)
        self.mydomain = domain
        self.myhost = host
        self.myport = port
        self.aor = 'sip:proxy@' + self.myhost + ':' + str(self.myport)
        self.myrr = 'sip:' + self.myhost + ':' + str(self.myport)
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.bind((host, port))
        self.timer_manager = TimerManager()
        self.trx_layer = SIPTransactionManager()

    # -----------------------------------------------------------------------
    def load(self, filename):
        if __debug__:
            print 'Proxy.load(', filename, ')'
        try:
            reader = csv.reader(open(filename, 'r'))
            for row in reader:
                if len(row) > 0:
                    self.locdata[row[0]] = UserData(row)
        except:
            print 'exception occured, failed to load ' + filename

    # -----------------------------------------------------------------------
    def run(self):
        if __debug__:
            print 'Proxy.run()'
        counter = 0
        while True:
            try:
                buf, src = self.sock.recvfrom(0xffff)
            except KeyboardInterrupt:
                sys.exit(1)
            except:
                continue
            print '< ' * 5, 'rcvd ', src[0], ':', str(src[1]), ' <' * 5
            print '#', counter
            print buf
            counter += 1
            try:
                self.proc_message(buf, src)
            except SentResponse:
                continue
            except Exception, e:
                if __debug__:
                    print 'exception occured:', e
                    continue

    # -----------------------------------------------------------------------
    def proc_message(self, buf, src):
        if __debug__:
            print 'Proxy.proc_message(...)'
        msg = SIPMessage(buf)
        if msg.method != '':
            msg.insert_received(src)
            self.proc_request(msg)
        else:
            self.proc_response(msg)

    # -----------------------------------------------------------------------
    def ins_auth_hdr(self, msg, hname):
        if __debug__:
            print 'Proxy.ins_auth_hdr(...)'
        pos = msg.search('call-id', 'i')
        cid = msg.hdrs[pos].vals[0]
        nonce = md5.new(md5.new(cid).hexdigest()).hexdigest()
        hvals = []
        hvals.append('Digest realm="' + self.myhost + ':'
                     + str(self.myport) + '"')
        hvals.append('nonce="' + nonce + '"')
        hdr = SIPHeader(hname, hvals)
        msg.hdrs.append(hdr)
        return msg

    # -----------------------------------------------------------------------
    def proc_register(self, msg):
        if __debug__:
            print 'Proxy.proc_register(...)'
        from_user, t0, t1 = msg.search_addr_spec('from', 'f')
        if not self.locdata.has_key(from_user):
            msg.conv2resp("403", "Forbidden")
            msg.hdrs.append(SIPHeader("Contact", [self.aor]))
            self.send(msg)
            return
        if self.locdata[from_user].passwd != '':
            if __debug__:
                print 'authentication required for ', from_user
            try:
                pos = msg.search('authorization')
                if not self.authenticate(str(msg.hdrs[pos]), 'REGISTER',
                                         self.locdata[from_user].passwd):
                    msg.conv2resp('401', 'Unauthorized')
                    msg.hdrs.append(SIPHeader('Contact', [self.aor]))
                    msg = self.ins_auth_hdr(msg, 'WWW-Authenticate')
                    self.send(msg)
                    return
            except NotFound:
                if __debug__:
                    print "WWW-Authenticate header not found"
                msg.conv2resp("401", "Unauthorized")
                msg.hdrs.append(SIPHeader("Contact", [self.aor]))
                msg = self.ins_auth_hdr(msg, "WWW-Authenticate")
                self.send(msg)
                return
        to_user, t0, t1 = msg.search_addr_spec("to", "t")
        if to_user != from_user:
            msg.conv2resp("403", "Forbidden")  # shuld be 404?
            msg.hdrs.append(SIPHeader("Contact", [self.aor]))
            self.send(msg)
            return
        t0, contact_addr, t1 = msg.search_name_addr("contact", "m")
        if __debug__:
            print "register ", to_user, " -> ", contact_addr
        self.locdata[to_user].contacts = [contact_addr]
        msg.conv2resp("200", "OK")
        msg.hdrs.append(SIPHeader("Contact", [contact_addr]))
        self.send(msg)

    # -----------------------------------------------------------------------
    def proc_request(self, msg):
        if __debug__:
            print "Proxy.proc_request(...)"
        user, host, port = self.parse_addr_spec(msg.requri)
        if __debug__:
            print user, host, port
        if((self.myhost == host and self.myport == port) or
            self.mydomain == host):
            if msg.method == "REGISTER":
                self.proc_register(msg)
                return
        msg = self.validate_request(msg)
        msg = self.preprocess_route_info(msg)
        if msg == None:
            return
        msg, targets = self.determine_request_targets(msg)
        if msg == None:
            return
        self.forward_request(msg, targets)

    # -----------------------------------------------------------------------
    def validate_request(self, msg):
        if __debug__:
            print "Proxy.validate_request(...)  RFC3261 16.3"
        msg = self.check_uri_scheme(msg)
        msg = self.check_max_forwards(msg)
        msg = self.check_proxy_require(msg)
        msg = self.check_proxy_authorization(msg)
        return msg

    # -----------------------------------------------------------------------
    def check_uri_scheme(self, msg):
        if __debug__:
            print "Proxy.check_uri_scheme(...)"
        m = re.match(r'((sip)|(tel)):', msg.requri)
        if m == None:
            if msg.method != "ACK":
                msg.conv2resp("416", "Unsupported URI Scheme")
                msg.hdrs.append(SIPHeader("Contact", [self.aor]))
                self.send(msg)
            raise SentReponse, "416 Unsupported URI Scheme"
        return msg

    # -----------------------------------------------------------------------
    def check_max_forwards(self, msg):
        if __debug__:
            print "Proxy.check_max_forwards(...)"
        try:
            pos = msg.search("max-forwards")
            if msg.hdrs[pos].vals[0] == "0":
                if __debug__:
                    print "Max-Forwards value == 0"
                if msg.method != "ACK":
                    msg.conv2resp("483", "Too Many Hops")
                    msg.hdrs.append(SIPHeader("Contact", [self.aor]))
                    self.send(msg)
                raise SentResponse, "483 Too Many Hops"
            msg.hdrs[pos].vals[0] = str(int(msg.hdrs[pos].vals[0]) - 1)
        except NotFound:
            msg.hdrs.append(SIPHeader("Max-Forwards", ["70"]))
        return msg

    # -----------------------------------------------------------------------
    def check_proxy_require(self, msg):
        if __debug__:
            print "Proxy.check_proxy_require(...)"
        try:
            pos = msg.search("proxy-require")
            if __debug__:
                "Proxy-Require header found"
            if msg.method != "ACK":
                pr = msg.hdrs[pos]
                pr.name = "Unsupported"
                msg.conv2resp("420", "Bad Extension")
                msg.hdrs.append(SIPHeader("Contact", [self.aor]))
                msg.hdrs.append(pr)
                self.send(msg)
            raise SentReponse, "sent 420 Bad Extension"
        except NotFound:
            pass # do nothing
        return msg

    # -----------------------------------------------------------------------
    def check_proxy_authorization(self, msg):
        if __debug__:
            print "Proxy.check_proxy_authorization(...)"
        if msg.method != "INVITE":
            return msg
        user, host, port = msg.search_addr_spec("from", "f")
        if host != self.myhost or port != self.myport:
            return msg
        if not self.locdata.has_key(user):
            msg.conv2resp("403", "Forbidden")
            msg.hdrs.append(SIPHeader("Contact", [self.aor]))
            self.send(msg)
            raise SentResponsee, "sent 403 Forbidden"
        passwd = self.locdata[user].passwd
        if passwd == "":
            return msg
        try:
            pos = msg.search("proxy-authorization")
            if self.authenticate(str(msg.hdrs[pos]), msg.method,
                                 passwd, msg.body):
                return msg
        except NotFound:
            pass
        msg.conv2resp("407", "Proxy Authentication Required")
        msg.hdrs.append(SIPHeader("Contact", [self.aor]))
        msg = self.ins_auth_hdr(msg, "Proxy-Authenticate")
        self.send(msg)
        raise SentResponse, "sent 407"

    # -----------------------------------------------------------------------
    def preprocess_route_info(self, msg):
        if __debug__:
            print "Proxy.preprocess_route(...)  RFC3261 16.4"
        if self.myrr == msg.requri:
            msg.requri = msg.delete_last("route")
        try:
            pos = msg.search("route")
            t0, top_route, t1 = self.parse_name_addr(msg.hdrs[pos].vals[0])
            if self.myrr == top_route:
                del(msg.hdrs[pos].vals[0])
                if len(msg.hdrs[pos]) == 0:
                    del(msg.hdrs[pos])
        except NotFound:
            pass
        return msg

    # -----------------------------------------------------------------------
    def determine_request_targets(self, msg):
        if __debug__:
            print "Proxy.determine_request_targets(...)  RFC3261 16.5"
        targets = []
        user, host, port = self.parse_addr_spec(msg.requri)
        if host != self.myhost or port != self.myport:
            if __debug__:
                print "not to me.."
            return msg, [msg.requri]
        if user == '':
            if __debug__:
                print "cannot find the user"
            return None, []
        if user in self.locdata:
            targets = self.locdata[user].contacts
        else: # not registered
            if __debug__:
                print "user not registered"
            if(msg.method != "ACK"):
                msg.conv2resp("404", "Not Found")
                msg.hdrs.append(SIPHeader("Contact", [self.aor]))
                self.send(msg)
            raise SentReponse, "404 Not Found"
        return msg, targets

    # -----------------------------------------------------------------------
    def forward_request(self, msg, targets):
        if __debug__:
            print "Proxy.forward_request(...)  RFC3261 16.6"
        org = msg
        for target in targets:
            msg.requri = target
            msg = self.insert_record_route(msg)
            msg = self.insert_via(msg, org.requri)
            self.send(msg)

    # -----------------------------------------------------------------------
    def insert_record_route(self, msg):
        if __debug__:
            print "Proxy.insert_record_route(...)  RFC3261 16.6.4"
        my_rr = "<" + self.myrr + ";lr>"
        try:
            pos = msg.search("record-route")
            msg.hdrs[pos].vals.insert(0, my_rr)
        except NotFound:
            msg.hdrs.append(SIPHeader("Record-Route", [my_rr]))
        return msg

    # -----------------------------------------------------------------------
    def insert_via(self, msg, org_requri):
        if __debug__:
            print "Proxy.insert_via(...)  RFC3261 16.6.8"
        branch = self.generate_branch(msg, org_requri)
        my_via = "SIP/2.0/UDP " + self.myhost + ":" + str(self.myport) + \
            ";branch=z9hG4bK" + branch
        try:
            pos = msg.search("via", "v")
            msg.hdrs[pos].vals.insert(0, my_via)
        except NotFound:
            msg.hdrs.append(SIPHeader("Via", [my_via]))
        return msg

    # -----------------------------------------------------------------------
    def generate_branch(self, msg, org_requri):
        pos = msg.search('via', 'v')
        via = msg.hdrs[pos].vals[0]
        m = re.search(r';branch=([^;]+)', via)
        if m != None:
            if m.group(1)[:7] == 'z9hG4bK':
                return md5.new(m.group(1)).hexdigest()
        pos = msg.search('from', 'f')
        buf = msg.hdrs[pos].vals[0]
        m = re.search(r';tag=([^;]+)', buf)
        if m != None:
            from_tag = m.group(1)
        else:
            from_tag = ""
        pos = msg.search("to", "t")
        buf = msg.hdrs[pos].vals[0]
        m = re.search(r";tag=([^;]+)", buf)
        if m != None:
            to_tag = m.group(1)
        else:
            to_tag = ""
        pos = msg.search("call-id", "i")
        cid = msg.hdrs[pos].vals[0]
        pos = msg.search("cseq")
        buf = msg.hdrs[pos].vals[0]
        m = re.match(r"(\d+)\s+[A-Z]+", buf)
        cseq_num = m.group(1)
        buf = ' '.join([via, from_tag, to_tag, cid, cseq_num, org_requri])
        return md5.new(buf).hexdigest()
    
    # -----------------------------------------------------------------------
    def proc_response(self, msg):
        if __debug__:
            print "Proxy.proc_response(...)  RFC3261  16.7"
        pos = msg.search("via", "v")
        del msg.hdrs[pos].vals[0]
        if len(msg.hdrs[pos].vals) == 0:
            del msg.hdrs[pos]
        self.send(msg)

    # -----------------------------------------------------------------------
    def send(self, msg):
        if __debug__:
            print "Proxy.send(...)"
        host, port = msg.get_dest()
        if host == self.myhost and port == self.myport:
            return
        buf = str(msg)
        try:
            self.sock.sendto(buf, (host, port))
        except:
            if __debug__:
                print 'failede to sendto'
            return
        print "> " * 5 + "sent " + host + ":" + str(port) + " >" * 5
        print buf


#############################################################################
if __name__ == "__main__":
    if len(sys.argv) != 5:
        print "\nUSAGE:\n\tproxy.py domain host port user.dat\n\n"
    else:
        px = Proxy()
        px.initialize(sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4])
        print "ok"
        px.run()


ユーザデータファイルはこんな感じ。

1111,pasword,sip:1111@127.0.0.1:5061
2222,,sip:2222@127.0.0.1:5061