操作系统:debian8.5_x64
freeswitch 版本 : 1.6.8

本文仅描述sip注册的简单场景,即话机直接向处于同一个局域网的fs进行注册。

SIP协议的消息结构

消息框架

SIP协议是基于文本的协议,SIP协议的消息都遵从一个统一的消息结构: 起始行(Start-Line)、一个或多个头域(Message-Header)、表明域结束的空行(CRLF),以及 可选的消息体(Message-Body)

Start-Line

* Message-Header

CRLF

[Message-Body]

消息头结构

SIP协议定义了大量的消息头域,但在一个基本SIP请求中至少应该包含以下几个消息体头域:

From : 请求发起端地址
To : 请求目的端地址
Call-ID : 呼叫标识
Contract :联系人信息
CSeq : 消息序号
Max-Forward :TTL,防止死循环
Via : 消息转发记录
Content-Length : 消息体长度

消息体结构

SIP协议并没有规定消息体的结构,对消息体的应用完全取决于应用自身。

sip协议REGISTER请求格式

  REGISTER sip:{remote_ip} SIP/2.0
Via: {viaInfo}
Max-Forwards:
From: {uacInfo}
To: {uasInfo}
Call-ID: {call_id}
User-Agent: {uaInfo}
CSeq: {csnum}
Contact: {contractInfo}
Expires: {expireSeconds}
Authorization: {authInfo}
Content-Length: {msgBodyLength}
  • 第1行的REGISTER表示这是一条注册消息
  • 第2行的via表示sip消息的路由
  • 第3行指出消息最多可以转发多少次,防止死循环
  • 第4行至第9行参考前面的消息头结构
  • 第10行说明本次注册的有效期,单位为秒
  • 第11行是sip认证信息,发送第一次sip register 请求时没有该字段
  • 第12行sip消息体的长度,一般为0

注册流程

终端向服务器发送REGISTER请求

sip消息示例:

REGISTER sip:192.168.168.85 SIP/2.0
Via: SIP/2.0/UDP 192.168.168.168:;branch=z9hG4bK-d87543-1a71103b47634958---d87543-;rport
Max-Forwards:
Contact: <sip:@192.168.168.168:;rinstance=196b0ce810f2e6f5>
To: ""<sip:@192.168.168.85>
From: ""<sip:@192.168.168.85>;tag=2d1fbf20
Call-ID: ZTRiYTBhZmVlYTM1ZDkxOWQ3OWNkNjkwMmYxMWI5Yjk.
CSeq: REGISTER
Expires:
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO
User-Agent: eyeBeam release 1010f stamp
Content-Length:

服务器回复401,并给出WWW-Authenticate信息

sofia协议栈:
tport :
tport_recv_event
=> tport_deliver
nta :
agent_recv_request
=> agent_aliases
nua :
nua_stack_process_request sofia 应用层:
sofia_reg_handle_sip_i_register
=> sofia_reg_handle_register_token
=> sofia_reg_auth_challenge (回复401)

sip消息示例:

SIP/2.0  Unauthorized
Via: SIP/2.0/UDP 192.168.168.168:;branch=z9hG4bK-d87543-1a71103b47634958---d87543-;rport=
From: "" <sip:@192.168.168.85>;tag=2d1fbf20
To: "" <sip:@192.168.168.85>;tag=m6ecFK1Fy3F0a
Call-ID: ZTRiYTBhZmVlYTM1ZDkxOWQ3OWNkNjkwMmYxMWI5Yjk.
CSeq: REGISTER
User-Agent: FreeSWITCH-mod_sofia/1.6.+git~20160505T153832Z~99de0ad502~64bit
Allow: INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE
Supported: timer, path, replaces
WWW-Authenticate: Digest realm="192.168.168.85", nonce="d54e4bb9-fc22-4e08-8b69-442e1b8774eb", algorithm=MD5, qop="auth"
Content-Length:

终端再次向服务器发送REGISTER请求,并携带认证信息

REGISTER sip:192.168.168.85 SIP/2.0
Via: SIP/2.0/UDP 192.168.168.168:;branch=z9hG4bK-d87543-6371fe0e115be271---d87543-;rport
Max-Forwards:
Contact: <sip:@192.168.168.168:;rinstance=196b0ce810f2e6f5>
To: ""<sip:@192.168.168.85>
From: ""<sip:@192.168.168.85>;tag=2d1fbf20
Call-ID: ZTRiYTBhZmVlYTM1ZDkxOWQ3OWNkNjkwMmYxMWI5Yjk.
CSeq: REGISTER
Expires:
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO
User-Agent: eyeBeam release 1010f stamp
Authorization: Digest username="",realm="192.168.168.85",nonce="d54e4bb9-fc22-4e08-8b69-442e1b8774eb",uri="sip:192.168.168.85",response="c46ae8e7eaa2ee63a1d61bf575d8c395",cnonce="71c1997e810fc38b53b97fbb33dc8b1e",nc=,qop=auth,algorithm=MD5
Content-Length:

服务器认证后回复200

fs处理过程描述

sofia协议栈:

tport :
tport_recv_event
=> tport_deliver
nta :
agent_recv_request
=> agent_aliases
nua :
nua_stack_process_request

sofia应用层:

sofia_reg_handle_sip_i_register
=> sofia_reg_handle_register_token
=> sofia_reg_parse_auth (执行认证操作) for_the_sake_of_interop标签附近

认证成功后发送200 OK给客户端。

认证算法描述

具体的认证过程可以参考sofia_reg_parse_auth函数,这里描述下sip注册的认证算法。
sip注册认证使用的是www-authenticate认证算法,具体可参考RFC2617文档。下面进行简单的描述

对用户名、认证域(realm)以及密码的合并值计算 MD5 哈希值,结果称为 HA1。
对HTTP方法以及URI的摘要的合并值计算 MD5 哈希值,例如,"REGISTER" 和 "sip:192.168.1.80",结果称为 HA2。
对 HA1、服务器密码随机数(nonce)、请求计数(nc)、客户端密码随机数(cnonce)、保护质量(qop)以及 HA2 的合并值计算 MD5 哈希值。结果即为客户端提供的 response 值。 response 值由三步计算而成。当多个数值合并的时候,使用冒号作为分割符。

计算HA1

HA1 = MD5(A1) = MD5(username:realm:password)

计算HA2

如果qop值为"auth"或未指定,那么HA2为

    HA2 = MD5(A2) = MD5(method:digestURI)

如果qop值为"auth-init",那么HA2为

    HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody))

计算response

如果qop值为"auth"或"auth-init",那么response为

    response = MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2)

如果qop未知道,那么response为

    response = MD5(HA1:nonce:HA2)

sip注册涉及数据库

  • sip_authentication
  • sip_registrations

注册过程模拟

python模拟注册过程

#! /usr/bin/env python
#-*- coding:utf-8 -*- # sip reg test
# E-Mail : Mike_Zhang@live.com import sys,socket,time,traceback
import uuid
import hashlib svrIp,svrPort = "192.168.168.85",5060
transportType = "udp"
localIp,localPort = "192.168.168.168",17061
uid,passwd = "",""
g_branch,g_callId = uuid.uuid1(),uuid.uuid1() def getRegHeader(seqNum):
retStr = "REGISTER sip:{remote_ip} SIP/2.0\r\n"
retStr += "Via: SIP/2.0/{transport} {local_ip}:{local_port};branch={branch}\r\n"
retStr += "Max-Forwards: 70\r\n"
retStr += "From: {uid} <sip:{uid}@{remote_ip}:{remote_port}>;tag={call_number}\r\n"
retStr += "To: {uid} <sip:{uid}@{remote_ip}:{remote_port}>\r\n"
retStr += "Call-ID: {call_id}\r\n"
retStr += "User-Agent: fs testing\r\n"
retStr += "CSeq: %d REGISTER\r\n"%seqNum
retStr += "Contact: sip:{uid}@{local_ip}:{local_port}\r\n"
retStr += "Expires: 3600\r\n"
return retStr def formatRegHeader(patternStr):
retstr = patternStr.format(
remote_ip=svrIp,
transport=transportType,
local_ip=localIp,
local_port=localPort,
branch=g_branch,
uid=uid,
remote_port=svrPort,
call_number=uid,
call_id=g_callId)
return retstr class SipRegObj(object):
def __init__(self,uid,passwd,
realm="",nonce="",algorithm="MD5",qop="auth"):
self.uid = uid # "1000"
self.password = passwd # "1234"
self.realm = realm
self.nonce = nonce
self.algorithm = algorithm
self.qop = qop
self.uri = ""
self.method = "REGISTER"
self.cnonce = ""
self.nc = ""
self.svrIp = svrIp
#self.transportType = "udp" def getReponse(self):
md5 = hashlib.md5
ha1 = md5("%s:%s:%s"%(self.uid,self.realm,self.password)).hexdigest()
ha2 = md5("%s:%s"%(self.method,self.uri)).hexdigest()
reponse = md5("%s:%s:%s:%s:%s:%s"%(ha1,self.nonce,self.nc,self.cnonce,self.qop,ha2)).hexdigest()
return reponse def getAuthStr(self):
self.uri,self.cnonce = ("sip:%s"%self.svrIp),uuid.uuid1()
retStr = ""
response = self.getReponse()
retStr += 'Authorization: Digest username="%s",realm="%s",nonce="%s",uri="%s",response="%s",cnonce="%s",nc=%s,qop=%s,algorithm=%s\r\n'%(
self.uid,self.realm,self.nonce,self.uri,response,self.cnonce,self.nc,self.qop,self.algorithm)
retStr += 'Content-Length: 0\r\n'
return retStr def genChallengeMsg(self):
# gen sip reg challenge message
str1=getRegHeader(1) + "Content-Length: 0\r\n"
return formatRegHeader(str1) def genAuthMsg(self,retstr1):
# parse data
data1 = retstr1.split("\r\n")[-4].split(": Digest")[1]
data1 = str(data1).replace('MD5','"MD5"')
tmpList = data1.split(",")
for item in tmpList :
#print item
arrtmp = item.split("=")
setattr(self,arrtmp[0].strip(),arrtmp[1].strip('"'))
# gen sip reg message with auth
str2=getRegHeader(2)
str2 = formatRegHeader(str2)
str2 += self.getAuthStr()
return str2 if __name__ == "__main__":
treg = SipRegObj(uid,passwd) client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.bind((localIp,localPort))
dstHost = (svrIp,svrPort) client.sendto(treg.genChallengeMsg(),dstHost)
print time.time(),' : send success' # get response (401 msg)
retstr1 = client.recv(1024)
print retstr1 client.sendto(treg.genAuthMsg(retstr1),dstHost)
print time.time(),' : send success' retstr2 = client.recv(1024)
print "return str : \r\n",retstr2 time.sleep(30)

本文github地址:

https://github.com/mike-zhang/mikeBlogEssays/blob/master/2016/20160912_freeswitch注册过程分析.md

欢迎补充

freeswitch注册过程分析的更多相关文章

  1. Dalvik虚拟机JNI方法的注册过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8923483 在前面一文中,我们分析了Dalvi ...

  2. dubbo作为消费者注册过程分析

    请支持原创: http://www.cnblogs.com/donlianli/p/3847676.html   作者当前分析的版本为2.5.x.作者在分析的时候,都是带着疑问去查看代码,debug进 ...

  3. dubbo作为消费者注册过程分析--????

    请支持原创: http://www.cnblogs.com/donlianli/p/3847676.html   作者当前分析的版本为2.5.x.作者在分析的时候,都是带着疑问去查看代码,debug进 ...

  4. 深入理解SpringCloud之Eureka注册过程分析

    eureka是一种去中心化的服务治理应用,其显著特点是既可以作为服务端又可以作为服务向自己配置的地址进行注册.那么这篇文章就来探讨一下eureka的注册流程. 一.Eureka的服务端 eureka的 ...

  5. 13、虚拟驱动vivi.c注册过程分析及怎么写V4L2驱动及启动过程

    UVC设备也是一个usb设备,在uvc_driver.c中的init函数会调用usb_register注册,根据id_table发送可支持的设备后调用probe函数,其会去uvc_register_c ...

  6. Dubbo源码剖析三之服务注册过程分析

    Dubbo源码剖析二之注册中心 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中对注册中心进行了简单的介绍,对Dubbo整合Zookeeper链接源码进行了详细分析.本文接着对服务注册过 ...

  7. freeswitch 注册错误408 - Request Timeout

    1.网络不通(可能虚拟机没打开网络) 2.防火墙没有过滤端口号(关闭防火墙) 3.IP地址错误

  8. FreeSWITCH 1.2.5.3 Step by Step Install

    Ubuntu: apt-get -y install build-essential automake autoconf git-core wget libtool apt-get -y instal ...

  9. Freeswitch 入门

    让我们从最初的运行开始,一步一步进入 FreeSWITCH 的神秘世界. 命令行参数 一般来说,FreeSWITCH 不需要任何命令行参数就可以启动,但在某些情况下,你需要以一些特殊的参数启动.在此, ...

随机推荐

  1. 站在风口,你或许就是那年薪20w+的程序猿

    最近面试了一些人,也在群上跟一些群友聊起,发现现在的互联网真是热,一些工作才两三年的期望的薪资都是十几K的起,这真是让我们这些早几年就成为程序猿的情何以堪!正所谓是站在风口上,猪也能飞起来!我在这里就 ...

  2. HTML5 <details> 标签

    HTML5 中新增的<details>标签允许用户创建一个可展开折叠的元件,让一段文字或标题包含一些隐藏的信息. 用法 一般情况下,details用来对显示在页面的内容做进一步骤解释.其展 ...

  3. xp搭建IIS出现HTTP 500错误-2147467259 (0x80004005)

    安装IIS后访问localhost页面, 提示The remote procedure call failed and did not execute,再刷新变为:-2147467259 (0x800 ...

  4. Oracle数据库自动备份SQL文本:Procedure存储过程,View视图,Function函数,Trigger触发器,Sequence序列号等

    功能:备份存储过程,视图,函数触发器,Sequence序列号等准备工作:--1.创建文件夹 :'E:/OracleBackUp/ProcBack';--文本存放的路径--2.执行:create or ...

  5. tornado template

    若果使用Tornado进行web开发可能会用到模板功能,页面继承,嵌套... 多页应用模板的处理多半依赖后端(SPA就可以动态加载局部视图),就算是RESTfull的API设计,也不妨碍同时提供部分模 ...

  6. 解析大型.NET ERP系统 单据编码功能实现

    单据编码是ERP系统中必备的功能,用于生成各种单据的流水号,常常借助于日期时间等字符来生成一个唯一的单据号码.从软件的角度来说,就是为生成数据表的主键值(参考编号),从用户的角度来说,就是给业务单据制 ...

  7. 【兼容写法】HttpServerUtility.Execute 在等待异步操作完成时被阻止。关键词:MVC,分部视图,异步

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html MVC6之前的版本,对分部视图的异步支持不是很好 问题: 视图里面有分布视图:@{ ...

  8. JavaScript权威设计--JavaScript函数(简要学习笔记十一)

    1.函数调用的四种方式 第三种:构造函数调用 如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内.这和函数调用和方法调用是一致的.但如果构造函数没有形参,JavaScri ...

  9. 让DIV中文字换行显示

    1. <style> div { white-space:normal; word-break:break-all; word-wrap:break-word; } </style& ...

  10. Oracle位图索引

    索引由KEY和Data组成 位图索引的KEY比普通非唯一性索引多包含一个组成部分,分区,分区是将数据按行由内部机制分段以达到比较好的检索效率 位图索引的Data中,该索引KEY中数据值在分区段中按行分 ...