miconda / sipexer Goto Github PK
View Code? Open in Web Editor NEWModern and flexible SIP/VoIP cli tool
License: GNU General Public License v3.0
Modern and flexible SIP/VoIP cli tool
License: GNU General Public License v3.0
I am trying to run webrtc calls using below command but facing error
**Below command is NOT working for sip call over WSS: **
./sipexer -invite -vl 3 -websocket-origin wss://10.10.10.10:443 -co -com -fuser alice -tuser bob -cb -xh "Max-Forwards:70" -ap "abab..." -ha1 -sw 10000 -ti true -sd -su wss:10.10.10.20:443
error - [error] [sipexer.go:2011] main.SIPExerSendWSS(): error: %!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)
Below command is working fine for sip call over UDP:
./sipexer -invite -vl 3 -co -com -fuser alice -tuser bob -cb -xh "Max-Forwards:70" -ap "abab..." -ha1 -sw 10000 -sd -su udp:10.10.10.40:5060
Hello, great utility!
Is there any way of controlling the retransmission time?
Thanks!
I know it's a long shot but I am wondering if there is a way currently (or it's an easy fix) to ignore TLS certifications validation.
I'm sorry that I'm not able to better define the reasoning, etc. on this, however the pre-compiled binary won't work on Alpine linux, and just compiling as indicated in the instructions doesn't work:
git clone https://github.com/miconda/sipexer
cd sipexer
go get ./...
go build .
Setting the env var CGO_ENABLED to 0 when building creates a binary that works (for me) in both the default alpine container as well
as my normal Debian distro:
CGO_ENABLED=0 go build .
Given that this works for me on both, I'm not sure if it's better to have the distributed binary and the instructions always reference this, OR to simply add a note in the installation section? Or maybe this is so obvious to someone working in Go that it's unnecessary?
Hello,
I'm getting this intermittently, can't figure out why...
[error] [sipexer.go:1389] main.SIPExerSendBytes(): error writing - write udp4 172.26.210.130:50600->172.26.209.100:5060: i/o timeout
when call is answered.
This is my command:
./sipexer -invite -vl 9 -co -com \
-fuser '+numberFrom' \
-tuser numberTo \
-rn numberTo \
-cb \
-sw 1000 -sd -timer-t1 60000 -su \
udp:172.26.209.100:5060
Thanks for the tool Daniel!
is this possible? registering and then sending a call? I haven't figured it out...
Thanks to @miconda for awesome tool,
I just found a minor issue with the sipexer use the map to store the extra-header via -xh, that cause for only the latest one can be processed.
Example:
[info] [sipexer.go:1260] main.SIPExerDialogLoop(): local socket address: 127.0.0.1:53256 (udp)
[info] [sipexer.go:1261] main.SIPExerDialogLoop(): local via address: 127.0.0.1:53256
[info] [sipexer.go:1262] main.SIPExerDialogLoop(): sending to udp 127.0.0.1:5060: [[---
INVITE sip:127.0.0.1:5060 SIP/2.0
Via: SIP/2.0/UDP 127.0.0.1:53256;rport;branch=z9hG4bKSG.256b7e98-b824-47ae-bac8-a2c3aadf9281
From: <sip:+9876543210@localhost>;tag=d3107dc4-7f35-481c-b58f-e351e2704027
To: <sip:alice@localhost>
Call-ID: f5ccddd4-da36-48d7-b218-63cff5cdca0c
CSeq: 275785 INVITE
Date: Wed, 25 May 2022 16:23:20 +07
User-Agent: SIPExer v1.0.0
Content-Length: 208
Diversion: <[email protected]>
Content-Type: application/sdp
v=0
o=sipexer 1653470600 1653470600 IN IP4 127.0.0.1
s=call
c=IN IP4 127.0.0.1
t=0 0
m=audio 37009 RTP 0 8 101
a=rtpmap:0 pcmu/8000
a=rtpmap:8 pcma/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
[info] [sipexer.go:1264] main.SIPExerDialogLoop(): ---]]
only the [email protected]
is processed. We expected that the message would be:
...
Diversion: <[email protected]>
Diversion: <[email protected]>
...
Reference:
https://datatracker.ietf.org/doc/html/rfc3261#section-7.3.1
With the Belle-sip server (and perhaps others), it seems that a SIP INFO must use the same authentication session as the previous SIP INVITE. Otherwise, the response to SIP INFO is 491.
This means that the Authentication digest value must be calculated with the same Nonce value used for INVITE and the Nonce count must be incremented.
For the moment, the implementation does not allow passing the Nonce count as a field (-field-val parameter). The value nc=00000001 is hardcoded
The only solution I see would be to calculate the entire Proxy-Authorization header before invoking sipexer and pass it with -extra-header (but that's overkill !)
It would be great if we could pass the authentication values (Nonce, Nonce count, Opaque, ...) from a previous SIP message when making a new call to sipexer
[EDIT] I'm currently trying to implement this
When using the -invite
flow, a 200 OK
is received by the sipexer UA with two RR hops:
Record-Route: <sip:GW_2:5060;lr=on;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;vsf=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AA-;dlgcor=ccf1.7e73;proxy_media=yes>
Record-Route: <sip:GW_1:5060;lr;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;dlgcor=ccf.8be1;fromcor=ejFwbUZxUmpUUFNBejFwbUZxU
UUFNBejFwbUZxUmpUUFM-;proxy_media=yes>
This is properly reversed in the order of the Route
headers constructed in the e2e ACK
/ subsequent in-dialog messages:
Route: <sip:GW_1:5060;lr;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;dlgcor=ccf.8be1;fromcor=ejFwbUZxUmpUUFNBejFwbUZxUmp
FNBejFwbUZxUmpUUFM-;proxy_media=yes>
Route: <sip:GW_2:5060;lr=on;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;vsf=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-;dlgcor=ccf1.7e73;proxy_media=yes>
However, if the RR set returned is compacted into a single value...
Record-Route: <sip:GW_2:5060;lr=on;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;vsf=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AA-;dlgcor=ccf1.7e73;proxy_media=yes>, <sip:GW_1:5060;lr;ftag=37ff691d-7bf2-446b-abaa-9c09fe12deb1;dlgcor=ccf.8be1;fromcor=ejFwbUZxUmpUUFNBejFwbUZxU
UUFNBejFwbUZxUmpUUFM-;proxy_media=yes>
Then this order is imitated, rather than reversed, in the subsequent Route
construction.
Hi,
Are there any options to ignore this?
$ ./sipexer tls:127.0.0.1:8088
[error] [sipexer.go:1939] main.SIPExerSendTLS(): error: tls: failed to verify certificate: x509: certificate signed by unknown authority
Thanks.
Seems like the client doesn't add the mandatory (by RFC3261) Max-Forwards
header.
When running multiple tcp commands in row. at least in my endeavours, I encounter some problems with hanging tcp connection
sipexer -message -mb 'Heipa hei hoo' -laddr :5060 tcp:10.250.39.62:5060
[info] [sipexer.go:1434] main.SIPExerDialogLoop(): local socket address: 10.250.39.36:5060 (tcp)
[info] [sipexer.go:1435] main.SIPExerDialogLoop(): local via address: 10.250.39.36:5060
[info] [sipexer.go:1436] main.SIPExerDialogLoop(): sending to tcp 10.250.39.62:5060: [[---
MESSAGE sip:10.250.39.62:5060;transport=tcp SIP/2.0
Via: SIP/2.0/TCP 10.250.39.36:5060;rport;branch=z9hG4bKSG.de99e435-7200-44bf-b954-4e8a7a8fdc4c
From: sip:alice@localhost;tag=a50e604c-99c4-4849-8ab2-538b6e809b9b
To: sip:bob@localhost
Call-ID: 363536fc-df2a-4c13-99c9-f09407766a8e
CSeq: 385409 MESSAGE
Date: Thu, 10 Nov 2022 14:01:12 UTC
User-Agent: SIPExer v1.0.3
Content-Length: 13
Content-Type: text/plain
Heipa hei hoo
[info] [sipexer.go:1438] main.SIPExerDialogLoop(): ---]]
[info] [sipexer.go:1489] main.SIPExerDialogLoop(): response-received: from=10.250.39.62:5060 bytes=304 data=[[---
SIP/2.0 403 Forbidden
Via: SIP/2.0/TCP 10.250.39.36:5060;rport;branch=z9hG4bKSG.de99e435-7200-44bf-b954-4e8a7a8fdc4c
To: sip:bob@localhost
From: sip:alice@localhost;tag=a50e604c-99c4-4849-8ab2-538b6e809b9b
CSeq: 385409 MESSAGE
Call-ID: 363536fc-df2a-4c13-99c9-f09407766a8e
Content-Length: 0
[info] [sipexer.go:1491] main.SIPExerDialogLoop(): ---]]
sipexer -message -mb 'Heipa hei hoo\n' -laddr :5060 tcp:10.250.39.62:5060
[error] [sipexer.go:1731] main.SIPExerSendTCP(): error: dial tcp4 :5060->10.250.39.62:5060: bind: address already in use
netstat -an | grep 10.250.39.62
tcp 0 0 10.250.39.36:5060 10.250.39.62:5060 TIME_WAIT
tested with:
go version go1.19.3 linux/amd64
on:
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
The SDP in the generated INVITE contains transport RTP in the media description line:
<m=audio 32962 RTP 0 8 101>
I think that the transport should be RTP/AVP instead of just RTP.
The "branch" value in the Via header is wrong for an ACK in response to an INVITE that requires authentication
The actual value is the one of the second INVITE (with auth) instead of the first one (without auth)
To comply with RFC3261#17.1.1.3, the ACK Via header must be the same as the corresponding INVITE message
As a result, the INVITE is finally rejected
The issue comes from seDlg.FirstRequest in SIPExerDialogLoop() being modified by SIPExerProcessResponse() before sgsip.SGSIPInviteToACKString() is called to send the ACK.
A quick and dirty fix is to make a deep clone of seDlg.FirstRequest and use this copy inthe call to sgsip.SGSIPInviteToACKString() but this is definitely not how this should be fixed.
# 1st INVITE message :
INVITE bob@localhost SIP/2.0
Via: SIP/2.0/UDP 172.17.0.20:42701;rport;branch=z9hG4bKSG.3d94bc0c-c3f8-4839-8b62-36fa47808d27
# This INVITE requires auth :
SIP/2.0 407 Proxy Authentication Required
Via: SIP/2.0/UDP 172.17.0.20:42701;rport=42701;branch=z9hG4bKSG.3d94bc0c-c3f8-4839-8b62-36fa47808d27
# ACK to INVITE with an incorrect branch value :
ACK bob@localhost SIP/2.0
Via: SIP/2.0/UDP 172.17.0.20:42701;rport;branch=z9hG4bKSG.35479688-1274-4aaf-b9b3-2f9bb4515ed9
#################### The branch value should be z9hG4bKSG.3d94bc0c-c3f8-4839-8b62-36fa47808d27
# 2nd INVITE with auth :
INVITE bob@localhost SIP/2.0
Via: SIP/2.0/UDP 172.17.0.20:42701;rport;branch=z9hG4bKSG.35479688-1274-4aaf-b9b3-2f9bb4515ed9
# As the ACK was not accepted, the INVITE is finally rejected :
SIP/2.0 407 Proxy Authentication Required
Via: SIP/2.0/UDP 172.17.0.20:42701;rport=42701;branch=z9hG4bKSG.3d94bc0c-c3f8-4839-8b62-36fa47808d27
Hi @miconda
Thank you for the great tool. I'm starting to like this tool more than sipping.
I'm running into a tiny issue using Sipexer over IPv6 - Below is the error:
` $./sipexer fe80::acdb:69ff:feb5:7eaf
[error] [sipexer.go:726] main.main(): invalid destination address: fe80::acdb:69ff:feb5:7eaf
[root@ opt]# ./sipexer [fe80::acdb:69ff:feb5:7eaf]
panic: runtime error: slice bounds out of range [:1] with length 0
goroutine 1 [running]:
github.com/miconda/sipexer/sgsip.SGAddrTypeEx({0x0, 0x0})
/github/workspace/sgsip/sgsip.go:239 +0xb7
github.com/miconda/sipexer/sgsip.SGSIPParseSocketAddress({0xfff83881, 0x1b}, 0xa43bd4c)
/github/workspace/sgsip/sgsip.go:356 +0x56
main.main()
/github/workspace/sipexer.go:724 +0xeb4
[root@opt]# ./sipexer [::1]
panic: runtime error: slice bounds out of range [:1] with length 0
goroutine 1 [running]:
github.com/miconda/sipexer/sgsip.SGAddrTypeEx({0x0, 0x0})
/github/workspace/sgsip/sgsip.go:239 +0xb7
github.com/miconda/sipexer/sgsip.SGSIPParseSocketAddress({0xffd2c897, 0x5}, 0x9c3bd4c)
/github/workspace/sgsip/sgsip.go:356 +0x56
main.main()
/github/workspace/sipexer.go:724 +0xeb4
[root@t]# ./sipexer sip:[fe80::acdb:69ff:feb5:7eaf]:5060
[error] [sipexer.go:1814] main.SIPExerSendUDP(): error: dial udp6 [fe80::acdb:69ff:feb5:7eaf]:5060: connect: invalid argument
[root@ opt]# ./sipexer udp:[fe80::acdb:69ff:feb5:7eaf]:5060
[error] [sipexer.go:1814] main.SIPExerSendUDP(): error: dial udp6 [fe80::acdb:69ff:feb5:7eaf]:5060: connect: invalid argument
`
Any help would be appreciated! :)
It seems to support TLS, would love test TLS using SIPEXER
Hey!
It looks like the -timeout
parameter does not work with TCP transport.
This can be easily reproduced:
# time sipexer -timeout 10000 tcp:8.8.8.8:5060
...
[error] [sipexer.go:1875] main.SIPExerSendTCP(): error: dial tcp4 8.8.8.8:5060: connect: connection timed out
0.01s user 0.00s system 0% cpu 2:11.44 total
# time sipexer -timeout 10000 udp:8.8.8.8:5060
...
[info] [sipexer.go:1616] main.SIPExerDialogLoop(): trying again - new timeout at 3500ms
[info] [sipexer.go:1616] main.SIPExerDialogLoop(): trying again - new timeout at 7500ms
[error] [sipexer.go:1619] main.SIPExerDialogLoop(): error reading - bytes 0 - <nil>
0.00s user 0.00s system 0% cpu 7.535 total
I seem to have a digest auth interop problem with FreeSWITCH that I can't quite comprehend:
Invocation:
# ./sipexer -vl 3 -timer-t1 2000 -register -contact-uri sip:[email protected]:5060 -com -ex 120 -fuser abalashov -tuser abalashov -ha1 -au abalashov -ap xxx -sd -nagios udp:sip.evaristesys.com
The 401 challenge:
WWW-Authenticate: Digest realm="sip.evaristesys.com",nonce="634df9bc-54d5-431f-8dc7-66cb556fe12e",algorithm=MD5,qop="auth"
The full REGISTER
response + challenge answer:
REGISTER sip:sip.evaristesys.com:5060 SIP/2.0
Via: SIP/2.0/UDP 172.30.106.189:55414;rport;branch=z9hG4bKSG.be1f3a00-c393-4f3b-8d31-82fe0af34011
From: <sip:[email protected]>;tag=6c5e9643-b2f8-4069-b756-9a731009cf3a
To: <sip:[email protected]>
Call-ID: 1b8fd706-ab3c-4c18-bfab-d39e7e382cd5
CSeq: 841081 REGISTER
Date: Tue, 15 Feb 2022 08:26:54 EST
Contact: <sip:[email protected]:5060>
Expires: 120
User-Agent: SIPExer v1.0.0
Content-Length: 0
Authorization: Digest username="abalashov", realm="sip.evaristesys.com", nonce="634df9bc-54d5-431f-8dc7-66cb556fe12e", uri="sip:sip.evaristesys.com:5060", cnonce="I4VYGmfe8VC9g+Li", nc=00000001, qop=auth, opaque="", algorithm=MD5, response="fae65c5bd2d12f84bbeaa467f3dc092e"
I stubbornly get a 403 Forbidden
from FreeSWITCH, without explanation.
I've compared with a working REGISTER flow from a Polycom; aside from the Authorization
response itself, the requests are indistinguishable.
As far as I can tell, the code does everything correctly:
// build digest response
cnonce := SIPExerRandomKey()
response := SIPExerHMD5(strings.Join([]string{HA1, hparams["nonce"], "00000001", cnonce, hparams["qop"], HA2}, ":"))
// build header body
AuthHeader = fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=00000001, qop=%s, opaque="%s", algorithm=MD5, response="%s"`,
username, hparams["realm"], hparams["nonce"], hparams["uri"], cnonce, hparams["qop"], hparams["opaque"], response)
Any pointers would be helpful! Thank you very much again for writing this amazing tool.
Scenario: I have a TLS SIP server, with self-signed certifcates. I specify the tls-certificate
and tls-key
in sipexer, but I still get the aforementioned error.
Some minor things:
2) In which cases the tls-key
could be useful ? Usually just the CAfile (in my case the self signed certificate) is enough but the code seems to require both.
3) specifying just the tls-certificate
seems to not be an opton: again according to code, one needs to specify both tls-certificate
and tls-key
, but help doesn't say anything about it.
4) Help neglects to specify that both certificate and key must be in PEM format, at least according to docs here (still doesn't work though).
# sipexer -register -vl 3 -co -com -ex 60 -fuser alice -cb -ap "abab..." -ha1 -laddr 222.22.10.11:5060 -sd udp:111.22.11.33:5060
[debug] [sipexer.go:705] main.main(): parsed socket address argument ({Val:udp:111.22.11.33:5060 Proto:udp Addr:111.22.11.33 Port:5060 PortNo:5060 AType:4 ProtoId
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x6b8220]
goroutine 19 [running]:
main.SIPExerSendUDP({{0x7ffd4242984d, 0x16}, {0x737de1, 0x3}, {0x7ffd42429851, 0xd}, {0x7ffd4242985f, 0x4}, 0x13c4, 0x4, ...}, ...)
/home/dmitry/build/sipexer/sipexer.go:1754 +0x360
created by main.main in goroutine 1
/home/dmitry/build/sipexer/sipexer.go:780 +0x1836
This crash happens when some other app is already binded to 222.22.10.11:5060.
When using TCP, sipexer seems to immediately re-transmit the INVITE upon receipt of 100 reply. This potentially results in a storm of packets. Note the lack of any wait time before retransmitting the INVITE.
10.174.5.101:44330 10.174.5.11:5060
──────────┬───────── ──────────┬─────────
11:27:56.826765 │ INVITE │
+0.000889 │ ──────────────────────────> │
11:27:56.827654 │ 100 Trying │
+0.000728 │ <────────────────────────── │
11:27:56.828382 │ INVITE │
+0.000100 │ ────────────────────────>>> │
11:27:56.828482 │ 100 Trying │
+0.000598 │ <<<──────────────────────── │
11:27:56.829080 │ INVITE │
+0.000080 │ ────────────────────────>>> │
11:27:56.829160 │ 100 Trying │
+0.000506 │ <<<──────────────────────── │
11:27:56.829666 │ INVITE │
+0.000086 │ ────────────────────────>>> │
11:27:56.829752 │ 100 Trying │
+0.000553 │ <<<──────────────────────── │
11:27:56.830305 │ INVITE │
+0.000067 │ ────────────────────────>>> │
11:27:56.830372 │ 100 Trying │
+0.000632 │ <<<──────────────────────── │
11:27:56.831004 │ INVITE │
. . .
Tested against Kamailio 5.6, just returning a 100:
#!KAMAILIO
loadmodule "sl"
request_route {
send_reply("100", "Trying");
exit;
}
Using the following sipexer command:
sipexer -invite -co -com -cb -fu 15555551000 -tu 15555551234 -sd -su tcp:proxy
Resulted in over 1,300 messages (sent and received so over 650 INVITEs retransmitted) in less than one second.
Changing the target from tcp:proxy
to udp:proxy
gives the expected behavior - sipexer receives the 100 and waits.
When receiving a non 200 reply for an INVITE sipexer sends a response, but the branch parameter of the via header does not match the branch parameter of the INVITE.
Given this minimal Kamailio config:
#!KAMAILIO
loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "pv.so"
request_route {
if ( $rm == "ACK" ) {
if( !t_check_trans() ) {
exit;
}
}
t_newtran();
t_reply("404","Not Found");
}
Sending an Invite from sipexer (where kamailio
resolves to the host running the kamailio.cfg above):
sipexer -invite kamailio
Has Kamailio repeatedly sending the 404 reply after sipexer sends an ACK. This appears to be due to the ACK's via: header having a different branch than the INVITE.
INVITE (minus SDP):
INVITE sip:kamailio:5060 SIP/2.0
Via: SIP/2.0/UDP 172.31.0.4:56595;rport;branch=z9hG4bKSG.f7f686a3-ce6d-45a1-95a7-42652740d210
From: <sip:alice@localhost>;tag=9dc18f12-c779-4b0e-b30e-681d0526c72e
To: <sip:bob@localhost>
Call-ID: 0418f8d8-d472-43ab-9dc2-786129278a1c
CSeq: 481768 INVITE
Date: Fri, 01 Apr 2022 16:30:13 CDT
User-Agent: SIPExer v1.0.0
Content-Length: 210
Content-Type: application/sdp
ACK:
ACK sip:kamailio:5060 SIP/2.0
Via: SIP/2.0/UDP 172.31.0.4:56595;rport;branch=z9hG4bKSG.cda7e72d-892c-4bee-ad36-cec0734d8c59
From: <sip:alice@localhost>;tag=9dc18f12-c779-4b0e-b30e-681d0526c72e
To: <sip:bob@localhost>;tag=a6a1c5f60faecf035a1ae5b6e96e979a-f254
Call-ID: 0418f8d8-d472-43ab-9dc2-786129278a1c
CSeq: 481768 ACK
Content-Length: 0
Call flow:
172.31.0.4:56595 172.31.0.3:5060
──────────┬───────── ──────────┬─────────
16:30:13.498783 │ INVITE │
+0.000956 │ ──────────────────────────> │
16:30:13.499739 │ 404 Not Found │
+0.000530 │ <────────────────────────── │
16:30:13.500269 │ ACK │
+0.447913 │ ──────────────────────────> │
16:30:13.948182 │ 404 Not Found │
+0.999823 │ <<<──────────────────────── │
16:30:14.948005 │ 404 Not Found │
+2.000091 │ <<<──────────────────────── │
16:30:16.948096 │ 404 Not Found │
│ <<<──────────────────────── │
First of all, I want to say thank you for creating sipexer, and it would be great if this can have audio support, to specific a wav file for sending audio
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.