mirror of
https://github.com/pion/webrtc.git
synced 2025-09-26 19:21:12 +08:00
Rough DTLS implementation
This commit is contained in:
@@ -3,22 +3,44 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/pions/pkg/stun"
|
||||
"github.com/pions/webrtc/internal/dtls"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
func packetHandler(relaySocket *ipv4.PacketConn, remoteKey [16]byte) {
|
||||
const MTU = 1500
|
||||
func packetHandler(conn *ipv4.PacketConn, srcString string, remoteKey [16]byte) {
|
||||
const MTU = 8192
|
||||
buffer := make([]byte, MTU)
|
||||
|
||||
var d *dtls.DTLSState
|
||||
for {
|
||||
n, _, srcAddr, _ := relaySocket.ReadFrom(buffer)
|
||||
n, _, srcAddr, _ := conn.ReadFrom(buffer)
|
||||
|
||||
if d != nil && d.HandleDTLSPacket(buffer, n) {
|
||||
fmt.Println("Handling DTLS")
|
||||
}
|
||||
|
||||
if packetType, err := stun.GetPacketType(buffer[:n]); err == nil && packetType == stun.PacketTypeSTUN {
|
||||
if d == nil {
|
||||
d, err = dtls.New(true, srcString, srcAddr.String())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
d.DoHandshake()
|
||||
fmt.Println("sending handshake")
|
||||
}
|
||||
} else {
|
||||
d.DoHandshake()
|
||||
fmt.Println("sending handshake")
|
||||
return
|
||||
}
|
||||
return
|
||||
if m, err := stun.NewMessage(buffer[:n]); err == nil && m.Class == stun.ClassRequest && m.Method == stun.MethodBinding {
|
||||
dstAddr := &stun.TransportAddr{IP: srcAddr.(*net.UDPAddr).IP, Port: srcAddr.(*net.UDPAddr).Port}
|
||||
err := stun.BuildAndSend(relaySocket, dstAddr, stun.ClassSuccessResponse, stun.MethodBinding, m.TransactionID,
|
||||
err := stun.BuildAndSend(conn, dstAddr, stun.ClassSuccessResponse, stun.MethodBinding, m.TransactionID,
|
||||
&stun.XorMappedAddress{
|
||||
XorAddress: stun.XorAddress{
|
||||
IP: dstAddr.IP,
|
||||
@@ -48,13 +70,17 @@ func udpListener(ip string, remoteKey [16]byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
relaySocket := ipv4.NewPacketConn(listener)
|
||||
err = relaySocket.SetControlMessage(ipv4.FlagDst, true)
|
||||
conn := ipv4.NewPacketConn(listener)
|
||||
err = conn.SetControlMessage(ipv4.FlagDst, true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
addr, err := stun.NewTransportAddr(listener.LocalAddr())
|
||||
go packetHandler(relaySocket, remoteKey)
|
||||
|
||||
srcString := ip + ":" + strconv.Itoa(addr.Port)
|
||||
|
||||
dtls.AddListener(srcString, conn)
|
||||
go packetHandler(conn, srcString, remoteKey)
|
||||
return addr.Port, err
|
||||
}
|
||||
|
417
internal/dtls/dtls.c
Normal file
417
internal/dtls/dtls.c
Normal file
@@ -0,0 +1,417 @@
|
||||
#include "dtls.h"
|
||||
|
||||
//recommended cipher suites.
|
||||
const char cipherlist[] =
|
||||
"ECDHE-RSA-AES128-GCM-SHA256:"
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256:"
|
||||
"ECDHE-RSA-AES256-GCM-SHA384:"
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384:"
|
||||
"DHE-RSA-AES128-GCM-SHA256:"
|
||||
"kEDH+AESGCM:"
|
||||
"ECDHE-RSA-AES128-SHA256:"
|
||||
"ECDHE-ECDSA-AES128-SHA256:"
|
||||
"ECDHE-RSA-AES128-SHA:"
|
||||
"ECDHE-ECDSA-AES128-SHA:"
|
||||
"ECDHE-RSA-AES256-SHA384:"
|
||||
"ECDHE-ECDSA-AES256-SHA384:"
|
||||
"ECDHE-RSA-AES256-SHA:"
|
||||
"ECDHE-ECDSA-AES256-SHA:"
|
||||
"DHE-RSA-AES128-SHA256:"
|
||||
"DHE-RSA-AES128-SHA:"
|
||||
"DHE-RSA-AES256-SHA256:"
|
||||
"DHE-RSA-AES256-SHA:"
|
||||
"!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK";
|
||||
|
||||
static inline bool str_isempty(const char* str) {
|
||||
return ((str == NULL) || (str[0] == '\0'));
|
||||
}
|
||||
|
||||
static inline const char* str_nullforempty(const char* str) {
|
||||
return (str_isempty(str) ? NULL : str);
|
||||
}
|
||||
|
||||
static inline BIO* dtls_sess_get_rbio(dtls_sess* sess) {
|
||||
return SSL_get_rbio(sess->ssl);
|
||||
}
|
||||
static inline BIO* dtls_sess_get_wbio(dtls_sess* sess) {
|
||||
return SSL_get_wbio(sess->ssl);
|
||||
}
|
||||
|
||||
srtp_key_material* srtp_get_key_material(dtls_sess* sess);
|
||||
void key_material_free(srtp_key_material* km);
|
||||
|
||||
static inline void dtls_sess_set_state(dtls_sess* sess,
|
||||
enum dtls_con_state state) {
|
||||
sess->state = state;
|
||||
}
|
||||
|
||||
static inline enum dtls_con_state dtls_sess_get_state(const dtls_sess* sess) {
|
||||
return sess->state;
|
||||
}
|
||||
|
||||
static inline void srtp_key_material_extract(const srtp_key_material* km,
|
||||
srtp_key_ptrs* ptrs) {
|
||||
if (km->ispassive == DTLS_CONSTATE_ACT) {
|
||||
ptrs->localkey = (km->material);
|
||||
ptrs->remotekey = ptrs->localkey + MASTER_KEY_LEN;
|
||||
ptrs->localsalt = ptrs->remotekey + MASTER_KEY_LEN;
|
||||
ptrs->remotesalt = ptrs->localsalt + MASTER_SALT_LEN;
|
||||
} else {
|
||||
ptrs->remotekey = (km->material);
|
||||
ptrs->localkey = ptrs->remotekey + MASTER_KEY_LEN;
|
||||
ptrs->remotesalt = ptrs->localkey + MASTER_KEY_LEN;
|
||||
ptrs->localsalt = ptrs->remotesalt + MASTER_SALT_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
SSL_VERIFY_CB(dtls_trivial_verify_callback) {
|
||||
// TODO: add actuall verify routines here, if needed.
|
||||
(void)preverify_ok;
|
||||
(void)ctx;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_CTX* dtls_ctx_init(int verify_mode, ssl_verify_cb* cb, const tlscfg* cfg) {
|
||||
SSL_CTX* ctx = SSL_CTX_new(DTLS_method());
|
||||
|
||||
SSL_CTX_set_read_ahead(ctx, true);
|
||||
SSL_CTX_set_ecdh_auto(ctx, true);
|
||||
SSL_CTX_set_verify(ctx,
|
||||
(verify_mode & DTLS_VERIFY_FINGERPRINT) ||
|
||||
(verify_mode & DTLS_VERIFY_CERTIFICATE)
|
||||
? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
|
||||
: SSL_VERIFY_NONE,
|
||||
!(verify_mode & DTLS_VERIFY_CERTIFICATE)
|
||||
? (cb ? cb : dtls_trivial_verify_callback)
|
||||
: NULL);
|
||||
|
||||
switch (cfg->profile) {
|
||||
case SRTP_PROFILE_AES128_CM_SHA1_80:
|
||||
SSL_CTX_set_tlsext_use_srtp(ctx, "SRTP_AES128_CM_SHA1_80");
|
||||
break;
|
||||
case SRTP_PROFILE_AES128_CM_SHA1_32:
|
||||
SSL_CTX_set_tlsext_use_srtp(ctx, "SRTP_AES128_CM_SHA1_32");
|
||||
break;
|
||||
default:
|
||||
SSL_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SSL_CTX_use_certificate(ctx, cfg->cert)) {
|
||||
SSL_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SSL_CTX_use_PrivateKey(ctx, cfg->pkey) ||
|
||||
!SSL_CTX_check_private_key(ctx)) {
|
||||
SSL_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!SSL_CTX_set_cipher_list(ctx, cfg->cipherlist)) {
|
||||
SSL_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
dtls_sess* dtls_sess_new(SSL_CTX* sslcfg, int con_state) {
|
||||
dtls_sess* sess = (dtls_sess*)calloc(1, sizeof(dtls_sess));
|
||||
BIO* rbio = NULL;
|
||||
BIO* wbio = NULL;
|
||||
|
||||
sess->state = con_state;
|
||||
|
||||
if (NULL == (sess->ssl = SSL_new(sslcfg))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (NULL == (rbio = BIO_new(BIO_s_mem()))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
BIO_set_mem_eof_return(rbio, -1);
|
||||
|
||||
if (NULL == (wbio = BIO_new(BIO_s_mem()))) {
|
||||
BIO_free(rbio);
|
||||
rbio = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
BIO_set_mem_eof_return(wbio, -1);
|
||||
|
||||
SSL_set_bio(sess->ssl, rbio, wbio);
|
||||
|
||||
if (sess->state == DTLS_CONSTATE_PASS) {
|
||||
SSL_set_accept_state(sess->ssl);
|
||||
} else {
|
||||
SSL_set_connect_state(sess->ssl);
|
||||
}
|
||||
sess->type = DTLS_CONTYPE_NEW;
|
||||
|
||||
pthread_mutex_init(&sess->lock, NULL);
|
||||
return sess;
|
||||
|
||||
error:
|
||||
if (sess->ssl != NULL) {
|
||||
SSL_free(sess->ssl);
|
||||
sess->ssl = NULL;
|
||||
}
|
||||
free(sess);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dtls_sess_free(dtls_sess* sess) {
|
||||
if (sess->ssl != NULL) {
|
||||
SSL_free(sess->ssl);
|
||||
sess->ssl = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&sess->lock);
|
||||
free(sess);
|
||||
}
|
||||
|
||||
extern void go_handle_sendto(const char* src, const char* dst, char* buf,
|
||||
int len);
|
||||
ptrdiff_t dtls_sess_send_pending(dtls_sess* sess, const char* src,
|
||||
const char* dst) {
|
||||
if (sess->ssl == NULL) {
|
||||
return -2;
|
||||
}
|
||||
BIO* wbio = dtls_sess_get_wbio(sess);
|
||||
size_t pending = BIO_ctrl_pending(wbio);
|
||||
size_t len = 0;
|
||||
if (pending > 0) {
|
||||
char* buf = malloc(pending);
|
||||
len = BIO_read(wbio, buf, pending);
|
||||
buf = realloc(buf, len);
|
||||
|
||||
go_handle_sendto(src, dst, buf, len);
|
||||
return len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptrdiff_t dtls_sess_put_packet(dtls_sess* sess, const char* src,
|
||||
const char* dst, const void* buf, size_t len) {
|
||||
if (sess->ssl == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptrdiff_t ret = 0;
|
||||
char dummy[len];
|
||||
|
||||
pthread_mutex_lock(&sess->lock);
|
||||
pthread_mutex_unlock(&sess->lock);
|
||||
|
||||
BIO* rbio = dtls_sess_get_rbio(sess);
|
||||
|
||||
if (sess->state == DTLS_CONSTATE_ACTPASS) {
|
||||
sess->state = DTLS_CONSTATE_PASS;
|
||||
SSL_set_accept_state(sess->ssl);
|
||||
}
|
||||
|
||||
dtls_sess_send_pending(sess, src, dst);
|
||||
|
||||
BIO_write(rbio, buf, len);
|
||||
ret = SSL_read(sess->ssl, dummy, len);
|
||||
|
||||
if ((ret < 0) && SSL_get_error(sess->ssl, ret) == SSL_ERROR_SSL) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dtls_sess_send_pending(sess, src, dst);
|
||||
|
||||
if (SSL_is_init_finished(sess->ssl)) {
|
||||
sess->type = DTLS_CONTYPE_EXISTING;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptrdiff_t dtls_do_handshake(dtls_sess* sess, const char* src, const char* dst) {
|
||||
if (sess->ssl == NULL ||
|
||||
(dtls_sess_get_state(sess) != DTLS_CONSTATE_ACT &&
|
||||
dtls_sess_get_state(sess) != DTLS_CONSTATE_ACTPASS)) {
|
||||
return -2;
|
||||
}
|
||||
if (dtls_sess_get_state(sess) == DTLS_CONSTATE_ACTPASS) {
|
||||
dtls_sess_set_state(sess, DTLS_CONSTATE_ACT);
|
||||
}
|
||||
SSL_do_handshake(sess->ssl);
|
||||
pthread_mutex_lock(&sess->lock);
|
||||
ptrdiff_t ret = dtls_sess_send_pending(sess, src, dst);
|
||||
pthread_mutex_unlock(&sess->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
srtp_key_material* srtp_get_key_material(dtls_sess* sess) {
|
||||
if (!SSL_is_init_finished(sess->ssl)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
srtp_key_material* km = calloc(1, sizeof(srtp_key_material));
|
||||
|
||||
if (!SSL_export_keying_material(sess->ssl, km->material, sizeof(km->material),
|
||||
"EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {
|
||||
key_material_free(km);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
km->ispassive = sess->state;
|
||||
|
||||
return km;
|
||||
}
|
||||
|
||||
void key_material_free(srtp_key_material* km) {
|
||||
memset(km->material, 0, sizeof(km->material));
|
||||
free(km);
|
||||
}
|
||||
|
||||
// function to print binary blobs as comma-separated hexadecimals.
|
||||
int fprinthex(FILE* fp, const char* prefix, const void* b, size_t l) {
|
||||
int totallen = 0;
|
||||
const char* finger = (const char*)b;
|
||||
const char* end = finger + l;
|
||||
totallen += fprintf(fp, "%s: %hhx", prefix, *(finger++));
|
||||
for (; finger != end; finger++) {
|
||||
totallen += fprintf(fp, ":%hhx", *finger);
|
||||
}
|
||||
totallen += fputs("\n\n", fp);
|
||||
return totallen;
|
||||
}
|
||||
|
||||
// function to specifically print content srtp_key_ptrs objects.
|
||||
int fprintkeymat(FILE* fp, const srtp_key_ptrs* ptrs) {
|
||||
return fputs("********\n", fp) +
|
||||
fprinthex(fp, "localkey", ptrs->localkey, MASTER_KEY_LEN) +
|
||||
fprinthex(fp, "remotekey", ptrs->remotekey, MASTER_KEY_LEN) +
|
||||
fprinthex(fp, "localsalt", ptrs->localsalt, MASTER_SALT_LEN) +
|
||||
fprinthex(fp, "remotesalt", ptrs->remotesalt, MASTER_SALT_LEN) +
|
||||
fputs("********\n", fp);
|
||||
}
|
||||
|
||||
// function to specifically print fingerprint of X509 objects.
|
||||
int fprintfinger(FILE* fp, const char* prefix, const X509* cert) {
|
||||
unsigned char fingerprint[EVP_MAX_MD_SIZE];
|
||||
unsigned int size = sizeof(fingerprint);
|
||||
memset(fingerprint, 0, sizeof(fingerprint));
|
||||
if (!X509_digest(cert, EVP_sha512(), fingerprint, &size) || size == 0) {
|
||||
fprintf(stderr, "Failed to generated fingerprint from X509 object %p\n",
|
||||
cert);
|
||||
return 0;
|
||||
}
|
||||
return fprinthex(fp, prefix, fingerprint, size);
|
||||
}
|
||||
|
||||
tlscfg* dtls_build_tlscfg(void* cert_data, int cert_data_size, void* key_data,
|
||||
int key_data_size) {
|
||||
tlscfg* cfg = (tlscfg*)calloc(1, sizeof(tlscfg));
|
||||
cfg->profile = SRTP_PROFILE_AES128_CM_SHA1_80;
|
||||
cfg->cipherlist = cipherlist;
|
||||
|
||||
BIO* bio = BIO_new_mem_buf(cert_data, cert_data_size);
|
||||
if (NULL == (cfg->cert = PEM_read_bio_X509(bio, NULL, NULL, NULL))) {
|
||||
fputs("Fail to parse certificate file!\n", stderr);
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
}
|
||||
fprintfinger(stdout, "Fingerprint of local cert is ", cfg->cert);
|
||||
BIO_free(bio);
|
||||
|
||||
bio = BIO_new_mem_buf(key_data, key_data_size);
|
||||
if (NULL == (cfg->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL))) {
|
||||
fputs("Fail to parse private key file!\n", stderr);
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
}
|
||||
BIO_free(bio);
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
SSL_CTX* dtls_build_sslctx(tlscfg* cfg) {
|
||||
if (cfg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return dtls_ctx_init(DTLS_VERIFY_FINGERPRINT, NULL, cfg);
|
||||
}
|
||||
|
||||
dtls_sess* dtls_build_session(SSL_CTX* cfg, bool is_server) {
|
||||
return dtls_sess_new(cfg, is_server);
|
||||
}
|
||||
|
||||
bool openssl_global_init() {
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
SSL_load_error_strings();
|
||||
return SSL_library_init();
|
||||
}
|
||||
|
||||
void dtls_session_cleanup(tlscfg* cfg, SSL_CTX* ssl_ctx,
|
||||
dtls_sess* dtls_session) {
|
||||
if (dtls_session) {
|
||||
dtls_sess_free(dtls_session);
|
||||
}
|
||||
if (ssl_ctx) {
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
}
|
||||
if (cfg) {
|
||||
if (cfg->cert) {
|
||||
X509_free(cfg->cert);
|
||||
}
|
||||
if (cfg->pkey) {
|
||||
EVP_PKEY_free(cfg->pkey);
|
||||
}
|
||||
|
||||
free(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
void dtls_handle_incoming(dtls_sess* sess, const char* src, const char* dst,
|
||||
void* buf, int len) {
|
||||
ptrdiff_t stat = dtls_sess_put_packet(sess, src, dst, buf, len);
|
||||
if (SSL_get_error(sess->ssl, stat) == SSL_ERROR_SSL) {
|
||||
fprintf(stderr,
|
||||
"DTLS failure occurred on dtls session %p due to reason '%s'\n",
|
||||
sess, ERR_reason_error_string(ERR_get_error()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sess->type == DTLS_CONTYPE_EXISTING) {
|
||||
X509* peercert = SSL_get_peer_certificate(sess->ssl);
|
||||
if (peercert == NULL) {
|
||||
fprintf(stderr,
|
||||
"No certificate was provided by the peer on dtls session %p\n",
|
||||
sess);
|
||||
return;
|
||||
}
|
||||
fprintfinger(stdout, "Fingerprint of peer's cert is ", peercert);
|
||||
X509_free(peercert);
|
||||
|
||||
srtp_key_material* km = srtp_get_key_material(sess);
|
||||
if (km == NULL) {
|
||||
fprintf(stderr,
|
||||
"Unable to extract SRTP keying material from dtls session %p\n",
|
||||
sess);
|
||||
return;
|
||||
}
|
||||
srtp_key_ptrs ptrs = {0, 0, 0, 0};
|
||||
srtp_key_material_extract(km, &ptrs);
|
||||
fprintkeymat(stdout, &ptrs);
|
||||
key_material_free(km);
|
||||
|
||||
/* if we are a server
|
||||
if(sess->ssl == NULL || !SSL_is_init_finished(sess->ssl)){
|
||||
return;
|
||||
}
|
||||
|
||||
SSL_clear(sess->ssl);
|
||||
if (sess->state == DTLS_CONSTATE_PASS) {
|
||||
SSL_set_accept_state(sess->ssl);
|
||||
} else {
|
||||
SSL_set_connect_state(sess->ssl);
|
||||
}
|
||||
sess->type = DTLS_CONTYPE_NEW;
|
||||
*/
|
||||
}
|
||||
}
|
124
internal/dtls/dtls.go
Normal file
124
internal/dtls/dtls.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package dtls
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I .
|
||||
#cgo LDFLAGS: -lcrypto -lssl
|
||||
|
||||
#include "dtls.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !C.openssl_global_init() {
|
||||
panic("Failed to initalize OpenSSL")
|
||||
}
|
||||
}
|
||||
|
||||
var webrtcPacketMTU int = 8192
|
||||
var listenerMap map[string]*ipv4.PacketConn = make(map[string]*ipv4.PacketConn)
|
||||
var listenerMapLock = &sync.Mutex{}
|
||||
|
||||
//export go_handle_sendto
|
||||
func go_handle_sendto(rawSrc *C.char, rawDst *C.char, rawBuf *C.char, rawBufLen C.int) {
|
||||
src := C.GoString(rawSrc)
|
||||
dst := C.GoString(rawDst)
|
||||
buf := []byte(C.GoStringN(rawBuf, rawBufLen))
|
||||
C.free(unsafe.Pointer(rawBuf))
|
||||
|
||||
listenerMapLock.Lock()
|
||||
defer listenerMapLock.Unlock()
|
||||
if conn, ok := listenerMap[src]; ok {
|
||||
strIp, strPort, err := net.SplitHostPort(dst)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
port, err := strconv.Atoi(strPort)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
_, err = conn.WriteTo(buf, nil, &net.UDPAddr{IP: net.ParseIP(strIp), Port: port})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Could not find ipv4.PacketConn for %s \n", src)
|
||||
}
|
||||
}
|
||||
|
||||
type DTLSState struct {
|
||||
tlscfg *_Ctype_struct_tlscfg
|
||||
sslctx *_Ctype_struct_ssl_ctx_st
|
||||
dtls_session *_Ctype_struct_dtls_sess
|
||||
rawSrc, rawDst *_Ctype_char
|
||||
keyRaw, certRaw unsafe.Pointer
|
||||
}
|
||||
|
||||
func New(isClient bool, src, dst string) (d *DTLSState, err error) {
|
||||
cert, err := ioutil.ReadFile("domain.crt")
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
key, err := ioutil.ReadFile("domain.key")
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
d = &DTLSState{
|
||||
rawSrc: C.CString(src),
|
||||
rawDst: C.CString(dst),
|
||||
certRaw: C.CBytes(cert),
|
||||
keyRaw: C.CBytes(key),
|
||||
}
|
||||
|
||||
d.tlscfg = C.dtls_build_tlscfg(d.certRaw, C.int(len(cert)), d.keyRaw, C.int(len(key)))
|
||||
d.sslctx = C.dtls_build_sslctx(d.tlscfg)
|
||||
d.dtls_session = C.dtls_build_session(d.sslctx, C.bool(!isClient))
|
||||
|
||||
return d, err
|
||||
}
|
||||
|
||||
func (d *DTLSState) Close() {
|
||||
C.free(unsafe.Pointer(d.certRaw))
|
||||
C.free(unsafe.Pointer(d.keyRaw))
|
||||
C.free(unsafe.Pointer(d.rawSrc))
|
||||
C.free(unsafe.Pointer(d.rawDst))
|
||||
C.dtls_session_cleanup(d.tlscfg, d.sslctx, d.dtls_session)
|
||||
}
|
||||
|
||||
func (d *DTLSState) HandleDTLSPacket(packet []byte, size int) bool {
|
||||
if packet[0] >= 20 && packet[0] <= 64 {
|
||||
packetRaw := C.CBytes(packet)
|
||||
C.dtls_handle_incoming(d.dtls_session, d.rawSrc, d.rawDst, packetRaw, C.int(size))
|
||||
C.free(unsafe.Pointer(packetRaw))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *DTLSState) DoHandshake() {
|
||||
C.dtls_do_handshake(d.dtls_session, d.rawSrc, d.rawDst)
|
||||
}
|
||||
|
||||
func AddListener(src string, conn *ipv4.PacketConn) {
|
||||
listenerMapLock.Lock()
|
||||
listenerMap[src] = conn
|
||||
listenerMapLock.Unlock()
|
||||
}
|
||||
|
||||
func RemoveListener(src string) {
|
||||
}
|
85
internal/dtls/dtls.h
Normal file
85
internal/dtls/dtls.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef DTLS_FOO_H
|
||||
#define DTLS_FOO_H
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define MASTER_KEY_LEN 16
|
||||
#define MASTER_SALT_LEN 14
|
||||
|
||||
enum dtls_verify_mode {
|
||||
DTLS_VERIFY_NONE = 0, /*!< Don't verify anything */
|
||||
DTLS_VERIFY_FINGERPRINT = (1 << 0), /*!< Verify the fingerprint */
|
||||
DTLS_VERIFY_CERTIFICATE = (1 << 1), /*!< Verify the certificate */
|
||||
};
|
||||
|
||||
enum dtls_con_state {
|
||||
DTLS_CONSTATE_ACT, //Endpoint is willing to inititate connections.
|
||||
DTLS_CONSTATE_PASS, //Endpoint is willing to accept connections.
|
||||
DTLS_CONSTATE_ACTPASS, //Endpoint is willing to both accept and initiate connections
|
||||
DTLS_CONSTATE_HOLDCONN, //Endpoint does not want the connection to be established right now
|
||||
};
|
||||
|
||||
enum dtls_con_type {
|
||||
DTLS_CONTYPE_NEW = false, //Endpoint wants to use a new connection
|
||||
DTLS_CONTYPE_EXISTING = true, //Endpoint wishes to use existing connection
|
||||
};
|
||||
|
||||
enum srtp_profile {
|
||||
SRTP_PROFILE_RESERVED=0,
|
||||
SRTP_PROFILE_AES128_CM_SHA1_80=1,
|
||||
SRTP_PROFILE_AES128_CM_SHA1_32=2,
|
||||
};
|
||||
|
||||
#define SSL_VERIFY_CB(x) int (x)(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
typedef SSL_VERIFY_CB(ssl_verify_cb);
|
||||
extern SSL_VERIFY_CB(dtls_trivial_verify_callback);
|
||||
|
||||
typedef struct tlscfg {
|
||||
X509* cert;
|
||||
EVP_PKEY* pkey;
|
||||
enum srtp_profile profile;
|
||||
const char* cipherlist;
|
||||
} tlscfg;
|
||||
|
||||
typedef struct dtls_sess {
|
||||
SSL* ssl;
|
||||
enum dtls_con_state state;
|
||||
enum dtls_con_type type;
|
||||
pthread_mutex_t lock;
|
||||
} dtls_sess;
|
||||
|
||||
typedef struct srtp_key_material{
|
||||
uint8_t material[(MASTER_KEY_LEN + MASTER_SALT_LEN) * 2];
|
||||
enum dtls_con_state ispassive;
|
||||
}srtp_key_material;
|
||||
|
||||
typedef struct srtp_key_ptrs {
|
||||
const uint8_t* localkey;
|
||||
const uint8_t* remotekey;
|
||||
const uint8_t* localsalt;
|
||||
const uint8_t* remotesalt;
|
||||
} srtp_key_ptrs;
|
||||
|
||||
bool openssl_global_init();
|
||||
|
||||
tlscfg *dtls_build_tlscfg(void *cert_data, int cert_data_size, void *key_data, int key_data_size);
|
||||
SSL_CTX *dtls_build_sslctx(tlscfg *cfg);
|
||||
dtls_sess* dtls_build_session(SSL_CTX* cfg, bool is_server);
|
||||
|
||||
ptrdiff_t dtls_do_handshake(dtls_sess* sess, const char *src, const char *dst);
|
||||
void dtls_handle_incoming(dtls_sess* sess, const char *src, const char *dst, void *buf, int len);
|
||||
|
||||
void dtls_session_cleanup(tlscfg *cfg, SSL_CTX *ssl_ctx, dtls_sess *dtls_session);
|
||||
|
||||
|
||||
#endif
|
@@ -67,7 +67,7 @@ func generateVP8OnlyAnswer() *sdp.SessionDescription {
|
||||
Attributes: []string{
|
||||
"ice-lite",
|
||||
// TODO kc5nra proper fingerprint
|
||||
"fingerprint:sha-512 4E:DD:25:41:95:51:85:B6:6A:29:42:FF:56:5B:41:47:2C:6C:67:36:7D:97:91:5A:65:C7:E1:76:1B:6E:D3:22:45:B4:9F:DF:EA:93:FF:20:F4:CB:A8:53:AF:50:DA:87:5A:C5:4C:5B:F6:4C:50:DC:D9:29:A3:C0:19:7A:17:48",
|
||||
"fingerprint:sha-512 BD:B3:A8:15:87:D4:BB:B3:79:B2:2D:2D:3C:F8:F4:CD:29:90:67:D6:FB:B4:E7:56:51:87:78:F8:59:41:7C:8D:80:1B:CD:10:38:8B:28:D5:21:A5:71:0B:FB:8A:AD:E5:FB:96:82:F8:18:59:78:B5:0A:53:4D:8A:38:9C:51:EB",
|
||||
"msid-semantic: WMS *",
|
||||
"group:BUNDLE video",
|
||||
},
|
||||
|
Reference in New Issue
Block a user