Support for WebRTC data tracks (output only, for now), rewrite of dTLS integration (now part of socket lib), support for multi-path WebRTC connections
This commit is contained in:
		
							parent
							
								
									56193f89b1
								
							
						
					
					
						commit
						3987cfec3f
					
				
					 16 changed files with 1303 additions and 811 deletions
				
			
		|  | @ -1,395 +0,0 @@ | ||||||
| #include "defines.h" |  | ||||||
| #include "dtls_srtp_handshake.h" |  | ||||||
| #include <algorithm> |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| /* Write mbedtls into a log file. */ |  | ||||||
| #define LOG_TO_FILE 0 |  | ||||||
| #if LOG_TO_FILE |  | ||||||
| #include <fstream> |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| static void print_mbedtls_error(int r); |  | ||||||
| static void print_mbedtls_debug_message(void *ctx, int level, const char *file, int line, const char *str); |  | ||||||
| static int on_mbedtls_wants_to_read(void *user, unsigned char *buf, |  | ||||||
|                                     size_t len); /* Called when mbedtls wants to read data from e.g. a socket. */ |  | ||||||
| static int on_mbedtls_wants_to_write(void *user, const unsigned char *buf, |  | ||||||
|                                      size_t len); /* Called when mbedtls wants to write data to e.g. a socket. */ |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| DTLSSRTPHandshake::DTLSSRTPHandshake() : cert(NULL), key(NULL), write_callback(NULL){ |  | ||||||
|   memset((void *)&entropy_ctx, 0x00, sizeof(entropy_ctx)); |  | ||||||
|   memset((void *)&rand_ctx, 0x00, sizeof(rand_ctx)); |  | ||||||
|   memset((void *)&ssl_ctx, 0x00, sizeof(ssl_ctx)); |  | ||||||
|   memset((void *)&ssl_conf, 0x00, sizeof(ssl_conf)); |  | ||||||
|   memset((void *)&cookie_ctx, 0x00, sizeof(cookie_ctx)); |  | ||||||
|   memset((void *)&timer_ctx, 0x00, sizeof(timer_ctx)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int DTLSSRTPHandshake::init(mbedtls_x509_crt *certificate, mbedtls_pk_context *privateKey, |  | ||||||
|                             int (*writeCallback)(const uint8_t *data, int *nbytes)){ |  | ||||||
| 
 |  | ||||||
|   int r = 0; |  | ||||||
|   mbedtls_ssl_srtp_profile srtp_profiles[] ={MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_80, |  | ||||||
|                                               MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_32}; |  | ||||||
| 
 |  | ||||||
|   if (!writeCallback){ |  | ||||||
|     FAIL_MSG("No writeCallack function given."); |  | ||||||
|     r = -3; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (!certificate){ |  | ||||||
|     FAIL_MSG("Given certificate is null."); |  | ||||||
|     r = -5; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (!privateKey){ |  | ||||||
|     FAIL_MSG("Given key is null."); |  | ||||||
|     r = -10; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   cert = certificate; |  | ||||||
|   key = privateKey; |  | ||||||
| 
 |  | ||||||
|   /* init the contexts */ |  | ||||||
|   mbedtls_entropy_init(&entropy_ctx); |  | ||||||
|   mbedtls_ctr_drbg_init(&rand_ctx); |  | ||||||
|   mbedtls_ssl_init(&ssl_ctx); |  | ||||||
|   mbedtls_ssl_config_init(&ssl_conf); |  | ||||||
|   mbedtls_ssl_cookie_init(&cookie_ctx); |  | ||||||
| 
 |  | ||||||
|   /* seed and setup the random number generator */ |  | ||||||
|   r = mbedtls_ctr_drbg_seed(&rand_ctx, mbedtls_entropy_func, &entropy_ctx, |  | ||||||
|                             (const unsigned char *)"mist-srtp", 9); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     r = -20; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* load defaults into our ssl_conf */ |  | ||||||
|   r = mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_DATAGRAM, |  | ||||||
|                                   MBEDTLS_SSL_PRESET_DEFAULT); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     r = -30; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   mbedtls_ssl_conf_authmode(&ssl_conf, MBEDTLS_SSL_VERIFY_NONE); |  | ||||||
|   mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &rand_ctx); |  | ||||||
|   mbedtls_ssl_conf_dbg(&ssl_conf, print_mbedtls_debug_message, stdout); |  | ||||||
|   mbedtls_ssl_conf_ca_chain(&ssl_conf, cert, NULL); |  | ||||||
|   mbedtls_ssl_conf_cert_profile(&ssl_conf, &mbedtls_x509_crt_profile_default); |  | ||||||
|   mbedtls_debug_set_threshold(10); |  | ||||||
| 
 |  | ||||||
|   /* enable SRTP */ |  | ||||||
|   r = mbedtls_ssl_conf_dtls_srtp_protection_profiles(&ssl_conf, srtp_profiles, |  | ||||||
|                                                      sizeof(srtp_profiles) / sizeof(srtp_profiles[0])); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     r = -40; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* cert certificate chain + key, so we can verify the client-hello signed data */ |  | ||||||
|   r = mbedtls_ssl_conf_own_cert(&ssl_conf, cert, key); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     r = -50; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* cookie setup (e.g. to prevent ddos). */ |  | ||||||
|   r = mbedtls_ssl_cookie_setup(&cookie_ctx, mbedtls_ctr_drbg_random, &rand_ctx); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     r = -60; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* register callbacks for dtls cookies (server only). */ |  | ||||||
|   mbedtls_ssl_conf_dtls_cookies(&ssl_conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &cookie_ctx); |  | ||||||
| 
 |  | ||||||
|   /* setup the ssl context for use. note that ssl_conf will be referenced internall by the context and therefore should be kept around. */ |  | ||||||
|   r = mbedtls_ssl_setup(&ssl_ctx, &ssl_conf); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     r = -70; |  | ||||||
|     goto error; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* set bio handlers */ |  | ||||||
|   mbedtls_ssl_set_bio(&ssl_ctx, (void *)this, on_mbedtls_wants_to_write, on_mbedtls_wants_to_read, NULL); |  | ||||||
| 
 |  | ||||||
|   /* set temp id, just adds some exta randomness */ |  | ||||||
|   { |  | ||||||
|     std::string remote_id = "mist"; |  | ||||||
|     r = mbedtls_ssl_set_client_transport_id(&ssl_ctx, (const unsigned char *)remote_id.c_str(), |  | ||||||
|                                             remote_id.size()); |  | ||||||
|     if (0 != r){ |  | ||||||
|       print_mbedtls_error(r); |  | ||||||
|       r = -80; |  | ||||||
|       goto error; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* set timer callbacks */ |  | ||||||
|   mbedtls_ssl_set_timer_cb(&ssl_ctx, &timer_ctx, mbedtls_timing_set_delay, mbedtls_timing_get_delay); |  | ||||||
| 
 |  | ||||||
|   write_callback = writeCallback; |  | ||||||
| 
 |  | ||||||
| error: |  | ||||||
| 
 |  | ||||||
|   if (r < 0){shutdown();} |  | ||||||
| 
 |  | ||||||
|   return r; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int DTLSSRTPHandshake::shutdown(){ |  | ||||||
| 
 |  | ||||||
|   /* cleanup the refs from the settings. */ |  | ||||||
|   cert = NULL; |  | ||||||
|   key = NULL; |  | ||||||
|   buffer.clear(); |  | ||||||
|   cipher.clear(); |  | ||||||
|   remote_key.clear(); |  | ||||||
|   remote_salt.clear(); |  | ||||||
|   local_key.clear(); |  | ||||||
|   local_salt.clear(); |  | ||||||
| 
 |  | ||||||
|   /* free our contexts; we do not free the `settings.cert` and `settings.key` as they are owned by the user of this class. */ |  | ||||||
|   mbedtls_entropy_free(&entropy_ctx); |  | ||||||
|   mbedtls_ctr_drbg_free(&rand_ctx); |  | ||||||
|   mbedtls_ssl_free(&ssl_ctx); |  | ||||||
|   mbedtls_ssl_config_free(&ssl_conf); |  | ||||||
|   mbedtls_ssl_cookie_free(&cookie_ctx); |  | ||||||
| 
 |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| int DTLSSRTPHandshake::parse(const uint8_t *data, size_t nbytes){ |  | ||||||
| 
 |  | ||||||
|   if (NULL == data){ |  | ||||||
|     ERROR_MSG("Given `data` is NULL."); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (0 == nbytes){ |  | ||||||
|     ERROR_MSG("Given nbytes is 0."); |  | ||||||
|     return -2; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (MBEDTLS_SSL_HANDSHAKE_OVER == ssl_ctx.state){ |  | ||||||
|     ERROR_MSG("Already finished the handshake."); |  | ||||||
|     return -3; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* copy incoming data into a temporary buffer which is read via our `bio` read function. */ |  | ||||||
|   int r = 0; |  | ||||||
|   std::copy(data, data + nbytes, std::back_inserter(buffer)); |  | ||||||
| 
 |  | ||||||
|   do{ |  | ||||||
| 
 |  | ||||||
|     r = mbedtls_ssl_handshake(&ssl_ctx); |  | ||||||
| 
 |  | ||||||
|     switch (r){ |  | ||||||
|     /* 0 = handshake done. */ |  | ||||||
|     case 0:{ |  | ||||||
|       if (0 != extractKeyingMaterial()){ |  | ||||||
|         ERROR_MSG("Failed to extract keying material after handshake was done."); |  | ||||||
|         return -2; |  | ||||||
|       } |  | ||||||
|       return 0; |  | ||||||
|     } |  | ||||||
|       /* see the dtls server example; this is used to prevent certain attacks (ddos) */ |  | ||||||
|     case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED:{ |  | ||||||
|       if (0 != resetSession()){ |  | ||||||
|         ERROR_MSG( |  | ||||||
|             "Failed to reset the session which is necessary when we need to verify the HELLO."); |  | ||||||
|         return -3; |  | ||||||
|       } |  | ||||||
|       break; |  | ||||||
|     } |  | ||||||
|     case MBEDTLS_ERR_SSL_WANT_READ:{ |  | ||||||
|       DONTEVEN_MSG( |  | ||||||
|           "mbedtls wants a bit more data before it can continue parsing the DTLS handshake."); |  | ||||||
|       break; |  | ||||||
|     } |  | ||||||
|     default:{ |  | ||||||
|       ERROR_MSG("A serious mbedtls error occured."); |  | ||||||
|       print_mbedtls_error(r); |  | ||||||
|       return -2; |  | ||||||
|     } |  | ||||||
|     } |  | ||||||
|   }while (MBEDTLS_ERR_SSL_WANT_WRITE == r); |  | ||||||
| 
 |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| int DTLSSRTPHandshake::resetSession(){ |  | ||||||
| 
 |  | ||||||
|   std::string remote_id = "mist"; /* @todo for now we hardcoded this... */ |  | ||||||
|   int r = 0; |  | ||||||
| 
 |  | ||||||
|   r = mbedtls_ssl_session_reset(&ssl_ctx); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   r = mbedtls_ssl_set_client_transport_id(&ssl_ctx, (const unsigned char *)remote_id.c_str(), |  | ||||||
|                                           remote_id.size()); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     return -2; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   buffer.clear(); |  | ||||||
| 
 |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
|   master key is 128 bits => 16 bytes. |  | ||||||
|   master salt is 112 bits => 14 bytes |  | ||||||
| */ |  | ||||||
| int DTLSSRTPHandshake::extractKeyingMaterial(){ |  | ||||||
| 
 |  | ||||||
|   int r = 0; |  | ||||||
|   uint8_t keying_material[MBEDTLS_DTLS_SRTP_MAX_KEY_MATERIAL_LENGTH] ={}; |  | ||||||
|   size_t keying_material_len = sizeof(keying_material); |  | ||||||
| 
 |  | ||||||
|   r = mbedtls_ssl_get_dtls_srtp_key_material(&ssl_ctx, keying_material, &keying_material_len); |  | ||||||
|   if (0 != r){ |  | ||||||
|     print_mbedtls_error(r); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* @todo following code is for server mode only */ |  | ||||||
|   mbedtls_ssl_srtp_profile srtp_profile = mbedtls_ssl_get_dtls_srtp_protection_profile(&ssl_ctx); |  | ||||||
|   switch (srtp_profile){ |  | ||||||
|   case MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_80:{ |  | ||||||
|     cipher = "SRTP_AES128_CM_SHA1_80"; |  | ||||||
|     break; |  | ||||||
|   } |  | ||||||
|   case MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_32:{ |  | ||||||
|     cipher = "SRTP_AES128_CM_SHA1_32"; |  | ||||||
|     break; |  | ||||||
|   } |  | ||||||
|   default:{ |  | ||||||
|     ERROR_MSG("Unhandled SRTP profile, cannot extract keying material."); |  | ||||||
|     return -6; |  | ||||||
|   } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   remote_key.assign((char *)(&keying_material[0]) + 0, 16); |  | ||||||
|   local_key.assign((char *)(&keying_material[0]) + 16, 16); |  | ||||||
|   remote_salt.assign((char *)(&keying_material[0]) + 32, 14); |  | ||||||
|   local_salt.assign((char *)(&keying_material[0]) + 46, 14); |  | ||||||
| 
 |  | ||||||
|   DONTEVEN_MSG("Extracted the DTLS SRTP keying material with cipher %s.", cipher.c_str()); |  | ||||||
|   DONTEVEN_MSG("Remote DTLS SRTP key size is %zu.", remote_key.size()); |  | ||||||
|   DONTEVEN_MSG("Remote DTLS SRTP salt size is %zu.", remote_salt.size()); |  | ||||||
|   DONTEVEN_MSG("Local DTLS SRTP key size is %zu.", local_key.size()); |  | ||||||
|   DONTEVEN_MSG("Local DTLS SRTP salt size is %zu.", local_salt.size()); |  | ||||||
| 
 |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
| 
 |  | ||||||
|   This function is called by mbedtls whenever it wants to read |  | ||||||
|   some data. The documentation states the following: "For DTLS, |  | ||||||
|   you need to provide either a non-NULL f_recv_timeout |  | ||||||
|   callback, or a f_recv that doesn't block." As this |  | ||||||
|   implementation is completely decoupled from any I/O and uses |  | ||||||
|   a "push" model instead of a "pull" model we have to copy new |  | ||||||
|   input bytes into a temporary buffer (see parse), but we act |  | ||||||
|   as if we were using a non-blocking socket, which means: |  | ||||||
| 
 |  | ||||||
|   - we return MBETLS_ERR_SSL_WANT_READ when there is no data left to read |  | ||||||
|   - when there is data in our temporary buffer, we read from that |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| static int on_mbedtls_wants_to_read(void *user, unsigned char *buf, size_t len){ |  | ||||||
| 
 |  | ||||||
|   DTLSSRTPHandshake *hs = static_cast<DTLSSRTPHandshake *>(user); |  | ||||||
|   if (NULL == hs){ |  | ||||||
|     ERROR_MSG("Failed to cast the user pointer into a DTLSSRTPHandshake."); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /* figure out how much we can read. */ |  | ||||||
|   if (hs->buffer.size() == 0){return MBEDTLS_ERR_SSL_WANT_READ;} |  | ||||||
| 
 |  | ||||||
|   size_t nbytes = hs->buffer.size(); |  | ||||||
|   if (nbytes > len){nbytes = len;} |  | ||||||
| 
 |  | ||||||
|   /* "read" into the given buffer. */ |  | ||||||
|   memcpy(buf, &hs->buffer[0], nbytes); |  | ||||||
|   hs->buffer.erase(hs->buffer.begin(), hs->buffer.begin() + nbytes); |  | ||||||
| 
 |  | ||||||
|   return (int)nbytes; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int on_mbedtls_wants_to_write(void *user, const unsigned char *buf, size_t len){ |  | ||||||
| 
 |  | ||||||
|   DTLSSRTPHandshake *hs = static_cast<DTLSSRTPHandshake *>(user); |  | ||||||
|   if (!hs){ |  | ||||||
|     FAIL_MSG("Failed to cast the user pointer into a DTLSSRTPHandshake."); |  | ||||||
|     return -1; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (!hs->write_callback){ |  | ||||||
|     FAIL_MSG("The `write_callback` member is NULL."); |  | ||||||
|     return -2; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   int nwritten = (int)len; |  | ||||||
|   if (0 != hs->write_callback(buf, &nwritten)){ |  | ||||||
|     FAIL_MSG("Failed to write some DTLS handshake data."); |  | ||||||
|     return -3; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (nwritten != (int)len){ |  | ||||||
|     FAIL_MSG("The DTLS-SRTP handshake listener MUST write all the data."); |  | ||||||
|     return -4; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return nwritten; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| static void print_mbedtls_error(int r){ |  | ||||||
|   char buf[1024] ={}; |  | ||||||
|   mbedtls_strerror(r, buf, sizeof(buf)); |  | ||||||
|   ERROR_MSG("mbedtls error: %s", buf); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void print_mbedtls_debug_message(void *ctx, int level, const char *file, int line, const char *str){ |  | ||||||
|   DONTEVEN_MSG("%s:%04d: %.*s", file, line, (int)strlen(str) - 1, str); |  | ||||||
| 
 |  | ||||||
| #if LOG_TO_FILE |  | ||||||
|   static std::ofstream ofs; |  | ||||||
|   if (!ofs.is_open()){ofs.open("mbedtls.log", std::ios::out);} |  | ||||||
|   if (!ofs.is_open()){return;} |  | ||||||
|   ofs << str; |  | ||||||
|   ofs.flush(); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* ---------------------------------------- */ |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <deque> |  | ||||||
| #include <mbedtls/certs.h> |  | ||||||
| #include <mbedtls/config.h> |  | ||||||
| #include <mbedtls/ctr_drbg.h> |  | ||||||
| #include <mbedtls/debug.h> |  | ||||||
| #include <mbedtls/entropy.h> |  | ||||||
| #include <mbedtls/error.h> |  | ||||||
| #include <mbedtls/ssl.h> |  | ||||||
| #include <mbedtls/ssl_cookie.h> |  | ||||||
| #include <mbedtls/timing.h> |  | ||||||
| #include <mbedtls/x509.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| class DTLSSRTPHandshake{ |  | ||||||
| public: |  | ||||||
|   DTLSSRTPHandshake(); |  | ||||||
|   int init(mbedtls_x509_crt *certificate, mbedtls_pk_context *privateKey, |  | ||||||
|            int (*writeCallback)(const uint8_t *data, |  | ||||||
|                                 int *nbytes)); // writeCallback should return 0 on succes < 0 on error.
 |  | ||||||
|                                                // nbytes holds the number of bytes to be sent and needs
 |  | ||||||
|                                                // to be set to the number of bytes actually sent.
 |  | ||||||
|   int shutdown(); |  | ||||||
|   int parse(const uint8_t *data, size_t nbytes); |  | ||||||
|   bool hasKeyingMaterial(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|   int extractKeyingMaterial(); |  | ||||||
|   int resetSession(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|   mbedtls_x509_crt *cert; /* Certificate, we do not own the key. Make sure it's kept alive during the livetime of this class instance. */ |  | ||||||
|   mbedtls_pk_context *key; /* Private key, we do not own the key. Make sure it's kept alive during the livetime of this class instance. */ |  | ||||||
|   mbedtls_entropy_context entropy_ctx; |  | ||||||
|   mbedtls_ctr_drbg_context rand_ctx; |  | ||||||
|   mbedtls_ssl_context ssl_ctx; |  | ||||||
|   mbedtls_ssl_config ssl_conf; |  | ||||||
|   mbedtls_ssl_cookie_ctx cookie_ctx; |  | ||||||
|   mbedtls_timing_delay_context timer_ctx; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|   int (*write_callback)(const uint8_t *data, int *nbytes); |  | ||||||
|   std::deque<uint8_t> buffer; /* Accessed from BIO callbback. We copy the bytes you pass into `parse()` into this
 |  | ||||||
|                                  temporary buffer which is read by a trigger to `mbedlts_ssl_handshake()`. */ |  | ||||||
|   std::string cipher; /* selected SRTP cipher. */ |  | ||||||
|   std::string remote_key; |  | ||||||
|   std::string remote_salt; |  | ||||||
|   std::string local_key; |  | ||||||
|   std::string local_salt; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
| 
 |  | ||||||
| inline bool DTLSSRTPHandshake::hasKeyingMaterial(){ |  | ||||||
|   return (0 != remote_key.size() && 0 != remote_salt.size() && 0 != local_key.size() && |  | ||||||
|           0 != local_salt.size()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* ----------------------------------------- */ |  | ||||||
|  | @ -12,7 +12,6 @@ headers = [ | ||||||
|   'comms.h', |   'comms.h', | ||||||
|   'config.h', |   'config.h', | ||||||
|   'defines.h', |   'defines.h', | ||||||
|   'dtls_srtp_handshake.h', |  | ||||||
|   'dtsc.h', |   'dtsc.h', | ||||||
|   'encryption.h', |   'encryption.h', | ||||||
|   'flv_tag.h', |   'flv_tag.h', | ||||||
|  | @ -69,7 +68,7 @@ install_headers(headers, subdir: 'mist') | ||||||
| extra_code = [] | extra_code = [] | ||||||
| 
 | 
 | ||||||
| if usessl | if usessl | ||||||
|   extra_code += ['dtls_srtp_handshake.cpp', 'stun.cpp', 'certificate.cpp', 'encryption.cpp',] |   extra_code += ['stun.cpp', 'certificate.cpp', 'encryption.cpp',] | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| libmist = library('mist', | libmist = library('mist', | ||||||
|  |  | ||||||
|  | @ -35,6 +35,8 @@ namespace SDP{ | ||||||
|       return "AAC"; |       return "AAC"; | ||||||
|     }else if (codec == "OPUS"){ |     }else if (codec == "OPUS"){ | ||||||
|       return "opus"; |       return "opus"; | ||||||
|  |     }else if (codec == "WEBRTC-DATACHANNEL"){ | ||||||
|  |       return "JSON"; | ||||||
|     }else if (codec == "ULPFEC"){ |     }else if (codec == "ULPFEC"){ | ||||||
|       return ""; |       return ""; | ||||||
|     }else if (codec == "RED"){ |     }else if (codec == "RED"){ | ||||||
|  | @ -67,6 +69,10 @@ namespace SDP{ | ||||||
|       return "MPA"; |       return "MPA"; | ||||||
|     }else if (codec == "AAC"){ |     }else if (codec == "AAC"){ | ||||||
|       return "MPEG4-GENERIC"; |       return "MPEG4-GENERIC"; | ||||||
|  |     }else if (codec == "JSON"){ | ||||||
|  |       return "WEBRTC-DATACHANNEL"; | ||||||
|  |     }else if (codec == "subtitle"){ | ||||||
|  |       return "WEBRTC-DATACHANNEL"; | ||||||
|     }else if (codec == "opus"){ |     }else if (codec == "opus"){ | ||||||
|       return "OPUS"; |       return "OPUS"; | ||||||
|     }else if (codec == "ULPFEC"){ |     }else if (codec == "ULPFEC"){ | ||||||
|  | @ -277,6 +283,8 @@ namespace SDP{ | ||||||
|       type = "audio"; |       type = "audio"; | ||||||
|     }else if (words[0] == "m=video"){ |     }else if (words[0] == "m=video"){ | ||||||
|       type = "video"; |       type = "video"; | ||||||
|  |     }else if (words[0] == "m=application"){ | ||||||
|  |       type = "meta"; | ||||||
|     }else{ |     }else{ | ||||||
|       ERROR_MSG("Unhandled media type: `%s`.", words[0].c_str()); |       ERROR_MSG("Unhandled media type: `%s`.", words[0].c_str()); | ||||||
|       return false; |       return false; | ||||||
|  | @ -289,6 +297,7 @@ namespace SDP{ | ||||||
|     for (size_t i = 3; i < words.size(); ++i){ |     for (size_t i = 3; i < words.size(); ++i){ | ||||||
|       SDP::MediaFormat format; |       SDP::MediaFormat format; | ||||||
|       format.payloadType = JSON::Value(words[i]).asInt(); |       format.payloadType = JSON::Value(words[i]).asInt(); | ||||||
|  |       if (words[i] == "webrtc-datachannel"){format.encodingName = "WEBRTC-DATACHANNEL";} | ||||||
|       formats[format.payloadType] = format; |       formats[format.payloadType] = format; | ||||||
|       if (!payloadTypes.empty()){payloadTypes += " ";} |       if (!payloadTypes.empty()){payloadTypes += " ";} | ||||||
|       payloadTypes += words[i]; |       payloadTypes += words[i]; | ||||||
|  | @ -711,17 +720,11 @@ namespace SDP{ | ||||||
|   static bool sdp_get_name_value_from_varval(const std::string &str, std::string &var, std::string &value){ |   static bool sdp_get_name_value_from_varval(const std::string &str, std::string &var, std::string &value){ | ||||||
| 
 | 
 | ||||||
|     if (str.empty()){ |     if (str.empty()){ | ||||||
|       ERROR_MSG("Cannot get `name` and `value` from string because the given string is empty. " |  | ||||||
|                 "String is: `%s`", |  | ||||||
|                 str.c_str()); |  | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     size_t pos = str.find("="); |     size_t pos = str.find("="); | ||||||
|     if (pos == std::string::npos){ |     if (pos == std::string::npos){ | ||||||
|       WARN_MSG("Cannot get `name` and `value` from string becuase it doesn't contain a `=` sign. " |  | ||||||
|                "String is: `%s`. Returning the string as is.", |  | ||||||
|                str.c_str()); |  | ||||||
|       value = str; |       value = str; | ||||||
|       return true; |       return true; | ||||||
|     } |     } | ||||||
|  | @ -776,7 +779,7 @@ namespace SDP{ | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Answer::Answer() |   Answer::Answer() | ||||||
|       : isAudioEnabled(false), isVideoEnabled(false), candidatePort(0), |       : isAudioEnabled(false), isVideoEnabled(false), isMetaEnabled(false), candidatePort(0), | ||||||
|         videoLossPrevention(SDP_LOSS_PREVENTION_NONE){} |         videoLossPrevention(SDP_LOSS_PREVENTION_NONE){} | ||||||
| 
 | 
 | ||||||
|   bool Answer::parseOffer(const std::string &sdp){ |   bool Answer::parseOffer(const std::string &sdp){ | ||||||
|  | @ -817,6 +820,15 @@ namespace SDP{ | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   bool Answer::enableMeta(const std::string &codecName){ | ||||||
|  |     if (!enableMedia("meta", codecName, answerMetaMedia, answerMetaFormat)){ | ||||||
|  |       DONTEVEN_MSG("Not enabling meta."); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     isMetaEnabled = true; | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   void Answer::setCandidate(const std::string &ip, uint16_t port){ |   void Answer::setCandidate(const std::string &ip, uint16_t port){ | ||||||
|     if (ip.empty()){WARN_MSG("Given candidate IP is empty. It's fine if you want to unset it.");} |     if (ip.empty()){WARN_MSG("Given candidate IP is empty. It's fine if you want to unset it.");} | ||||||
|     candidateIP = ip; |     candidateIP = ip; | ||||||
|  | @ -934,7 +946,7 @@ namespace SDP{ | ||||||
| 
 | 
 | ||||||
|       bool isEnabled = false; |       bool isEnabled = false; | ||||||
|       std::vector<uint8_t> supportedPayloadTypes; |       std::vector<uint8_t> supportedPayloadTypes; | ||||||
|       if (type != "audio" && type != "video"){continue;} |       if (type != "audio" && type != "video" && type != "meta"){continue;} | ||||||
| 
 | 
 | ||||||
|       // port = 9 (default), port = 0 (disable this media)
 |       // port = 9 (default), port = 0 (disable this media)
 | ||||||
|       if (type == "audio"){ |       if (type == "audio"){ | ||||||
|  | @ -947,6 +959,10 @@ namespace SDP{ | ||||||
|         fmtMedia = &answerVideoFormat; |         fmtMedia = &answerVideoFormat; | ||||||
|         fmtRED = media->getFormatForEncodingName("RED"); |         fmtRED = media->getFormatForEncodingName("RED"); | ||||||
|         fmtULPFEC = media->getFormatForEncodingName("ULPFEC"); |         fmtULPFEC = media->getFormatForEncodingName("ULPFEC"); | ||||||
|  |       }else if (type == "meta"){ | ||||||
|  |         isEnabled = isMetaEnabled; | ||||||
|  |         media = &answerMetaMedia; | ||||||
|  |         fmtMedia = &answerMetaFormat; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!media){ |       if (!media){ | ||||||
|  | @ -975,10 +991,17 @@ namespace SDP{ | ||||||
|       } |       } | ||||||
|       std::string payloadTypes = ss.str(); |       std::string payloadTypes = ss.str(); | ||||||
| 
 | 
 | ||||||
|  |       std::string protocol = "UDP/TLS/RTP/SAVPF"; | ||||||
|  |       if (type == "meta"){ | ||||||
|  |         protocol = "UDP/DTLS/SCTP"; | ||||||
|  |         payloadTypes = "webrtc-datachannel"; | ||||||
|  |         type = "application"; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       if (isEnabled){ |       if (isEnabled){ | ||||||
|         addLine("m=%s 9 UDP/TLS/RTP/SAVPF %s", type.c_str(), payloadTypes.c_str()); |         addLine("m=%s 9 %s %s", type.c_str(), protocol.c_str(), payloadTypes.c_str()); | ||||||
|       }else{ |       }else{ | ||||||
|         addLine("m=%s %u UDP/TLS/RTP/SAVPF %s", type.c_str(), 0, mediaOffer.payloadTypes.c_str()); |         addLine("m=%s %u %s %s", type.c_str(), 0, protocol.c_str(), mediaOffer.payloadTypes.c_str()); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       addLine("c=IN IP4 0.0.0.0"); |       addLine("c=IN IP4 0.0.0.0"); | ||||||
|  | @ -996,9 +1019,14 @@ namespace SDP{ | ||||||
|       addLine("a=fingerprint:sha-256 %s", fingerprint.c_str()); |       addLine("a=fingerprint:sha-256 %s", fingerprint.c_str()); | ||||||
|       addLine("a=ice-ufrag:%s", fmtMedia->iceUFrag.c_str()); |       addLine("a=ice-ufrag:%s", fmtMedia->iceUFrag.c_str()); | ||||||
|       addLine("a=ice-pwd:%s", fmtMedia->icePwd.c_str()); |       addLine("a=ice-pwd:%s", fmtMedia->icePwd.c_str()); | ||||||
|       addLine("a=rtcp-mux"); |       if (type == "application"){ | ||||||
|       addLine("a=rtcp-rsize"); |         addLine("a=sctp-port:5000"); | ||||||
|       addLine("a=%s", fmtMedia->rtpmap.c_str()); |         addLine("a=max-message-size:262144"); | ||||||
|  |       }else{ | ||||||
|  |         addLine("a=rtcp-mux"); | ||||||
|  |         addLine("a=rtcp-rsize"); | ||||||
|  |         addLine("a=%s", fmtMedia->rtpmap.c_str()); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       // BEGIN FEC/RTX: testing with just FEC or RTX
 |       // BEGIN FEC/RTX: testing with just FEC or RTX
 | ||||||
|       if ((videoLossPrevention & SDP_LOSS_PREVENTION_ULPFEC) && fmtRED && fmtULPFEC){ |       if ((videoLossPrevention & SDP_LOSS_PREVENTION_ULPFEC) && fmtRED && fmtULPFEC){ | ||||||
|  | @ -1136,14 +1164,11 @@ namespace SDP{ | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     INFO_MSG("Enabling media for codec: %s", format->encodingName.c_str()); |  | ||||||
| 
 |  | ||||||
|     outMedia = *media; |     outMedia = *media; | ||||||
|     outFormat = *format; |     outFormat = *format; | ||||||
|     outFormat.rtcpFormats.clear(); |     outFormat.rtcpFormats.clear(); | ||||||
|     outFormat.icePwd = generateIcePwd(); |     outFormat.icePwd = generateIcePwd(); | ||||||
|     outFormat.iceUFrag = generateIceUFrag(); |     outFormat.iceUFrag = generateIceUFrag(); | ||||||
| 
 |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -167,6 +167,7 @@ namespace SDP{ | ||||||
|     bool hasAudio(); ///< Check if the offer has audio.
 |     bool hasAudio(); ///< Check if the offer has audio.
 | ||||||
|     bool enableVideo(const std::string &codecName); |     bool enableVideo(const std::string &codecName); | ||||||
|     bool enableAudio(const std::string &codecName); |     bool enableAudio(const std::string &codecName); | ||||||
|  |     bool enableMeta(const std::string &codecName); | ||||||
|     void setCandidate(const std::string &ip, uint16_t port); |     void setCandidate(const std::string &ip, uint16_t port); | ||||||
|     void setFingerprint(const std::string &fingerprintSha); ///< Set the SHA265 that represents the
 |     void setFingerprint(const std::string &fingerprintSha); ///< Set the SHA265 that represents the
 | ||||||
|                                                             ///< certificate that is used with DTLS.
 |                                                             ///< certificate that is used with DTLS.
 | ||||||
|  | @ -189,10 +190,13 @@ namespace SDP{ | ||||||
|     SDP::Session sdpOffer; |     SDP::Session sdpOffer; | ||||||
|     SDP::Media answerVideoMedia; |     SDP::Media answerVideoMedia; | ||||||
|     SDP::Media answerAudioMedia; |     SDP::Media answerAudioMedia; | ||||||
|  |     SDP::Media answerMetaMedia; | ||||||
|     SDP::MediaFormat answerVideoFormat; |     SDP::MediaFormat answerVideoFormat; | ||||||
|     SDP::MediaFormat answerAudioFormat; |     SDP::MediaFormat answerAudioFormat; | ||||||
|  |     SDP::MediaFormat answerMetaFormat; | ||||||
|     bool isAudioEnabled; |     bool isAudioEnabled; | ||||||
|     bool isVideoEnabled; |     bool isVideoEnabled; | ||||||
|  |     bool isMetaEnabled; | ||||||
|     std::string candidateIP; ///< We use rtcp-mux and BUNDLE; so only one candidate necessary.
 |     std::string candidateIP; ///< We use rtcp-mux and BUNDLE; so only one candidate necessary.
 | ||||||
|     uint16_t candidatePort;  ///< We use rtcp-mux and BUNDLE; so only one candidate necessary.
 |     uint16_t candidatePort;  ///< We use rtcp-mux and BUNDLE; so only one candidate necessary.
 | ||||||
|     std::string fingerprint; |     std::string fingerprint; | ||||||
|  |  | ||||||
							
								
								
									
										645
									
								
								lib/socket.cpp
									
										
									
									
									
								
							
							
						
						
									
										645
									
								
								lib/socket.cpp
									
										
									
									
									
								
							|  | @ -14,6 +14,7 @@ | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | #include <fstream> | ||||||
| 
 | 
 | ||||||
| #define BUFFER_BLOCKSIZE 4096 // set buffer blocksize to 4KiB
 | #define BUFFER_BLOCKSIZE 4096 // set buffer blocksize to 4KiB
 | ||||||
| 
 | 
 | ||||||
|  | @ -1603,35 +1604,197 @@ int Socket::Server::getSocket(){ | ||||||
|   return sock; |   return sock; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | static int dTLS_recv(void *s, unsigned char *buf, size_t len){ | ||||||
|  |   return ((Socket::UDPConnection*)s)->dTLSRead(buf, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int dTLS_send(void *s, const unsigned char *buf, size_t len){ | ||||||
|  |   return ((Socket::UDPConnection*)s)->dTLSWrite(buf, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /// Create a new UDP Socket.
 | /// Create a new UDP Socket.
 | ||||||
| /// Will attempt to create an IPv6 UDP socket, on fail try a IPV4 UDP socket.
 | /// Will attempt to create an IPv6 UDP socket, on fail try a IPV4 UDP socket.
 | ||||||
| /// If both fail, prints an DLVL_FAIL debug message.
 | /// If both fail, prints an DLVL_FAIL debug message.
 | ||||||
| /// \param nonblock Whether the socket should be nonblocking.
 | /// \param nonblock Whether the socket should be nonblocking.
 | ||||||
| Socket::UDPConnection::UDPConnection(bool nonblock){ | Socket::UDPConnection::UDPConnection(bool nonblock){ | ||||||
|  |   init(nonblock); | ||||||
|  | }// Socket::UDPConnection UDP Contructor
 | ||||||
|  | 
 | ||||||
|  | void Socket::UDPConnection::init(bool _nonblock, int _family){ | ||||||
|   lastPace = 0; |   lastPace = 0; | ||||||
|   boundPort = 0; |   boundPort = 0; | ||||||
|   family = AF_INET6; |   family = _family; | ||||||
|   sock = socket(AF_INET6, SOCK_DGRAM, 0); |   hasDTLS = false; | ||||||
|   if (sock == -1){ |   isConnected = false; | ||||||
|  |   wasEncrypted = false; | ||||||
|  |   pretendReceive = false; | ||||||
|  |   sock = socket(family, SOCK_DGRAM, 0); | ||||||
|  |   if (sock == -1 && family == AF_INET6){ | ||||||
|     sock = socket(AF_INET, SOCK_DGRAM, 0); |     sock = socket(AF_INET, SOCK_DGRAM, 0); | ||||||
|     family = AF_INET; |     family = AF_INET; | ||||||
|   } |   } | ||||||
|   if (sock == -1){ |   if (sock == -1){ | ||||||
|     FAIL_MSG("Could not create UDP socket: %s", strerror(errno)); |     FAIL_MSG("Could not create UDP socket: %s", strerror(errno)); | ||||||
|   }else{ |   }else{ | ||||||
|     if (nonblock){setBlocking(!nonblock);} |     isBlocking = !_nonblock; | ||||||
|  |     if (_nonblock){setBlocking(!_nonblock);} | ||||||
|     checkRecvBuf(); |     checkRecvBuf(); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     // Allow address re-use
 | ||||||
|  |     int on = 1; | ||||||
|  |     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   up = 0; |   up = 0; | ||||||
|   down = 0; |   down = 0; | ||||||
|   destAddr = 0; |   destAddr = 0; | ||||||
|   destAddr_size = 0; |   destAddr_size = 0; | ||||||
|  |   recvAddr = 0; | ||||||
|  |   recvAddr_size = 0; | ||||||
|  |   hasReceiveData = false; | ||||||
| #ifdef __CYGWIN__ | #ifdef __CYGWIN__ | ||||||
|   data.allocate(SOCKETSIZE); |   data.allocate(SOCKETSIZE); | ||||||
| #else | #else | ||||||
|   data.allocate(2048); |   data.allocate(2048); | ||||||
| #endif | #endif | ||||||
| }// Socket::UDPConnection UDP Contructor
 | } | ||||||
|  | 
 | ||||||
|  | void Socket::UDPConnection::initDTLS(mbedtls_x509_crt *cert, mbedtls_pk_context *key){ | ||||||
|  |   hasDTLS = true; | ||||||
|  |   nextDTLSRead = 0; | ||||||
|  |   nextDTLSReadLen = 0; | ||||||
|  | 
 | ||||||
|  |   int r = 0; | ||||||
|  |   char mbedtls_msg[1024]; | ||||||
|  | 
 | ||||||
|  |   // Null out the contexts before use
 | ||||||
|  |   memset((void *)&entropy_ctx, 0x00, sizeof(entropy_ctx)); | ||||||
|  |   memset((void *)&rand_ctx, 0x00, sizeof(rand_ctx)); | ||||||
|  |   memset((void *)&ssl_ctx, 0x00, sizeof(ssl_ctx)); | ||||||
|  |   memset((void *)&ssl_conf, 0x00, sizeof(ssl_conf)); | ||||||
|  |   memset((void *)&cookie_ctx, 0x00, sizeof(cookie_ctx)); | ||||||
|  |   memset((void *)&timer_ctx, 0x00, sizeof(timer_ctx)); | ||||||
|  |   // Initialize contexts
 | ||||||
|  |   mbedtls_entropy_init(&entropy_ctx); | ||||||
|  |   mbedtls_ctr_drbg_init(&rand_ctx); | ||||||
|  |   mbedtls_ssl_init(&ssl_ctx); | ||||||
|  |   mbedtls_ssl_config_init(&ssl_conf); | ||||||
|  |   mbedtls_ssl_cookie_init(&cookie_ctx); | ||||||
|  | 
 | ||||||
|  |   /* seed and setup the random number generator */ | ||||||
|  |   r = mbedtls_ctr_drbg_seed(&rand_ctx, mbedtls_entropy_func, &entropy_ctx, (const unsigned char *)"mist-srtp", 9); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     FAIL_MSG("dTLS could not init drbg seed: %s", mbedtls_msg); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /* load defaults into our ssl_conf */ | ||||||
|  |   r = mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_DATAGRAM, | ||||||
|  |                                   MBEDTLS_SSL_PRESET_DEFAULT); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     FAIL_MSG("dTLS could not set defaults: %s", mbedtls_msg); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   mbedtls_ssl_conf_authmode(&ssl_conf, MBEDTLS_SSL_VERIFY_NONE); | ||||||
|  |   mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &rand_ctx); | ||||||
|  |   mbedtls_ssl_conf_ca_chain(&ssl_conf, cert, NULL); | ||||||
|  |   mbedtls_ssl_conf_cert_profile(&ssl_conf, &mbedtls_x509_crt_profile_default); | ||||||
|  |   //mbedtls_ssl_conf_dbg(&ssl_conf, print_mbedtls_debug_message, stdout);
 | ||||||
|  |   //mbedtls_debug_set_threshold(10);
 | ||||||
|  | 
 | ||||||
|  |   // enable SRTP support (non-fatal on error)
 | ||||||
|  |   mbedtls_ssl_srtp_profile srtpPro[] ={MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_80, MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_32}; | ||||||
|  |   r = mbedtls_ssl_conf_dtls_srtp_protection_profiles(&ssl_conf, srtpPro, sizeof(srtpPro) / sizeof(srtpPro[0])); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     WARN_MSG("dTLS could not set SRTP profiles: %s", mbedtls_msg); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /* cert certificate chain + key, so we can verify the client-hello signed data */ | ||||||
|  |   r = mbedtls_ssl_conf_own_cert(&ssl_conf, cert, key); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     FAIL_MSG("dTLS could not set certificate: %s", mbedtls_msg); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // cookie setup (to prevent ddos, server-only)
 | ||||||
|  |   r = mbedtls_ssl_cookie_setup(&cookie_ctx, mbedtls_ctr_drbg_random, &rand_ctx); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     FAIL_MSG("dTLS could not set SSL cookie: %s", mbedtls_msg); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   mbedtls_ssl_conf_dtls_cookies(&ssl_conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &cookie_ctx); | ||||||
|  | 
 | ||||||
|  |   // setup the ssl context
 | ||||||
|  |   r = mbedtls_ssl_setup(&ssl_ctx, &ssl_conf); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     FAIL_MSG("dTLS could not setup: %s", mbedtls_msg); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // set input/output callbacks
 | ||||||
|  |   mbedtls_ssl_set_bio(&ssl_ctx, (void *)this, dTLS_send, dTLS_recv, NULL); | ||||||
|  |   mbedtls_ssl_set_timer_cb(&ssl_ctx, &timer_ctx, mbedtls_timing_set_delay, mbedtls_timing_get_delay); | ||||||
|  | 
 | ||||||
|  |   // set transport ID (non-fatal on error)
 | ||||||
|  |   r = mbedtls_ssl_set_client_transport_id(&ssl_ctx, (const unsigned char *)"mist", 4); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     WARN_MSG("dTLS could not set transport ID: %s", mbedtls_msg); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Socket::UDPConnection::deinitDTLS(){ | ||||||
|  |   if (hasDTLS){ | ||||||
|  |     mbedtls_entropy_free(&entropy_ctx); | ||||||
|  |     mbedtls_ctr_drbg_free(&rand_ctx); | ||||||
|  |     mbedtls_ssl_free(&ssl_ctx); | ||||||
|  |     mbedtls_ssl_config_free(&ssl_conf); | ||||||
|  |     mbedtls_ssl_cookie_free(&cookie_ctx); | ||||||
|  |     hasDTLS = true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Socket::UDPConnection::dTLSRead(unsigned char *buf, size_t _len){ | ||||||
|  |   if (!nextDTLSReadLen){return MBEDTLS_ERR_SSL_WANT_READ;} | ||||||
|  |   size_t len = _len; | ||||||
|  |   if (len > nextDTLSReadLen){len = nextDTLSReadLen;} | ||||||
|  |   memcpy(buf, nextDTLSRead, len); | ||||||
|  |   nextDTLSReadLen = 0; | ||||||
|  |   return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Socket::UDPConnection::dTLSWrite(const unsigned char *buf, size_t len){ | ||||||
|  |   sendPaced((const char *)buf, len, false); | ||||||
|  |   return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Socket::UDPConnection::dTLSReset(){ | ||||||
|  |   char mbedtls_msg[1024]; | ||||||
|  |   int r = mbedtls_ssl_session_reset(&ssl_ctx); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     FAIL_MSG("dTLS could not reset session: %s", mbedtls_msg); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // set transport ID (non-fatal on error)
 | ||||||
|  |   r = mbedtls_ssl_set_client_transport_id(&ssl_ctx, (const unsigned char *)"mist", 4); | ||||||
|  |   if (r){ | ||||||
|  |     mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |     WARN_MSG("dTLS could not set transport ID: %s", mbedtls_msg); | ||||||
|  |   } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| ///Checks if the UDP receive buffer is at least 1 mbyte, attempts to increase and warns user through log message on failure.
 | ///Checks if the UDP receive buffer is at least 1 mbyte, attempts to increase and warns user through log message on failure.
 | ||||||
| void Socket::UDPConnection::checkRecvBuf(){ | void Socket::UDPConnection::checkRecvBuf(){ | ||||||
|  | @ -1681,27 +1844,23 @@ void Socket::UDPConnection::checkRecvBuf(){ | ||||||
| /// Copies a UDP socket, re-allocating local copies of any needed structures.
 | /// Copies a UDP socket, re-allocating local copies of any needed structures.
 | ||||||
| /// The data/data_size/data_len variables are *not* copied over.
 | /// The data/data_size/data_len variables are *not* copied over.
 | ||||||
| Socket::UDPConnection::UDPConnection(const UDPConnection &o){ | Socket::UDPConnection::UDPConnection(const UDPConnection &o){ | ||||||
|   lastPace = 0; |   init(!o.isBlocking, o.family); | ||||||
|   boundPort = 0; |   INFO_MSG("Copied socket of type %s", addrFam(o.family)); | ||||||
|   family = AF_INET6; |  | ||||||
|   sock = socket(AF_INET6, SOCK_DGRAM, 0); |  | ||||||
|   if (sock == -1){ |  | ||||||
|     sock = socket(AF_INET, SOCK_DGRAM, 0); |  | ||||||
|     family = AF_INET; |  | ||||||
|   } |  | ||||||
|   if (sock == -1){FAIL_MSG("Could not create UDP socket: %s", strerror(errno));} |  | ||||||
|   checkRecvBuf(); |  | ||||||
|   up = 0; |  | ||||||
|   down = 0; |  | ||||||
|   if (o.destAddr && o.destAddr_size){ |   if (o.destAddr && o.destAddr_size){ | ||||||
|     destAddr = malloc(o.destAddr_size); |     destAddr = malloc(o.destAddr_size); | ||||||
|     destAddr_size = o.destAddr_size; |     destAddr_size = o.destAddr_size; | ||||||
|     if (destAddr){memcpy(destAddr, o.destAddr, o.destAddr_size);} |     if (destAddr){memcpy(destAddr, o.destAddr, o.destAddr_size);} | ||||||
|   }else{ |  | ||||||
|     destAddr = 0; |  | ||||||
|     destAddr_size = 0; |  | ||||||
|   } |   } | ||||||
|   data.allocate(2048); |   if (o.recvAddr && o.recvAddr_size){ | ||||||
|  |     recvAddr = malloc(o.recvAddr_size); | ||||||
|  |     recvAddr_size = o.recvAddr_size; | ||||||
|  |     if (recvAddr){memcpy(recvAddr, o.recvAddr, o.recvAddr_size);} | ||||||
|  |   } | ||||||
|  |   if (o.data.size()){ | ||||||
|  |     data.assign(o.data, o.data.size()); | ||||||
|  |     pretendReceive = true; | ||||||
|  |   } | ||||||
|  |   hasReceiveData = o.hasReceiveData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Close the UDP socket
 | /// Close the UDP socket
 | ||||||
|  | @ -1720,8 +1879,35 @@ Socket::UDPConnection::~UDPConnection(){ | ||||||
|     free(destAddr); |     free(destAddr); | ||||||
|     destAddr = 0; |     destAddr = 0; | ||||||
|   } |   } | ||||||
|  |   if (recvAddr){ | ||||||
|  |     free(recvAddr); | ||||||
|  |     recvAddr = 0; | ||||||
|  |   } | ||||||
|  |   deinitDTLS(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | bool Socket::UDPConnection::operator==(const Socket::UDPConnection& b) const{ | ||||||
|  |   // UDP sockets are equal if they refer to the same underlying socket or are both closed
 | ||||||
|  |   if (sock == b.sock){return true;} | ||||||
|  |   // If either is closed (and the other is not), not equal.
 | ||||||
|  |   if (sock == -1 || b.sock == -1){return false;} | ||||||
|  |   size_t recvSize = recvAddr_size; | ||||||
|  |   if (b.recvAddr_size < recvSize){recvSize = b.recvAddr_size;} | ||||||
|  |   size_t destSize = destAddr_size; | ||||||
|  |   if (b.destAddr_size < destSize){destSize = b.destAddr_size;} | ||||||
|  |   // They are equal if they hold the same local and remote address.
 | ||||||
|  |   if (recvSize && destSize && destAddr && b.destAddr && recvAddr && b.recvAddr){ | ||||||
|  |     if (!memcmp(recvAddr, b.recvAddr, recvSize) && !memcmp(destAddr, b.destAddr, destSize)){ | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // All other cases, not equal
 | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Socket::UDPConnection::operator bool() const{return sock != -1;} | ||||||
|  | 
 | ||||||
| // Sets socket family type (to IPV4 or IPV6) (AF_INET=2, AF_INET6=10)
 | // Sets socket family type (to IPV4 or IPV6) (AF_INET=2, AF_INET6=10)
 | ||||||
| void Socket::UDPConnection::setSocketFamily(int AF_TYPE){\ | void Socket::UDPConnection::setSocketFamily(int AF_TYPE){\ | ||||||
|   INFO_MSG("Switching UDP socket from %s to %s", addrFam(family), addrFam(AF_TYPE)); |   INFO_MSG("Switching UDP socket from %s to %s", addrFam(family), addrFam(AF_TYPE)); | ||||||
|  | @ -1742,6 +1928,22 @@ void Socket::UDPConnection::allocateDestination(){ | ||||||
|       ((struct sockaddr_in *)destAddr)->sin_family = AF_UNSPEC; |       ((struct sockaddr_in *)destAddr)->sin_family = AF_UNSPEC; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   if (recvAddr && recvAddr_size < sizeof(sockaddr_in6)){ | ||||||
|  |     free(recvAddr); | ||||||
|  |     recvAddr = 0; | ||||||
|  |   } | ||||||
|  |   if (!recvAddr){ | ||||||
|  |     recvAddr = malloc(sizeof(sockaddr_in6)); | ||||||
|  |     if (recvAddr){ | ||||||
|  |       recvAddr_size = sizeof(sockaddr_in6); | ||||||
|  |       memset(recvAddr, 0, sizeof(sockaddr_in6)); | ||||||
|  |       ((struct sockaddr_in *)recvAddr)->sin_family = AF_UNSPEC; | ||||||
|  |     } | ||||||
|  |     const int opt = 1; | ||||||
|  |     if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))){ | ||||||
|  |       WARN_MSG("Could not set PKTINFO to 1!"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Stores the properties of the receiving end of this UDP socket.
 | /// Stores the properties of the receiving end of this UDP socket.
 | ||||||
|  | @ -1788,6 +1990,11 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ | ||||||
|       close(); |       close(); | ||||||
|       family = rp->ai_family; |       family = rp->ai_family; | ||||||
|       sock = socket(family, SOCK_DGRAM, 0); |       sock = socket(family, SOCK_DGRAM, 0); | ||||||
|  |       { | ||||||
|  |         // Allow address re-use
 | ||||||
|  |         int on = 1; | ||||||
|  |         setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | ||||||
|  |       } | ||||||
|       checkRecvBuf(); |       checkRecvBuf(); | ||||||
|       if (boundPort){ |       if (boundPort){ | ||||||
|         INFO_MSG("Rebinding to %s:%d %s", boundAddr.c_str(), boundPort, boundMulti.c_str()); |         INFO_MSG("Rebinding to %s:%d %s", boundAddr.c_str(), boundPort, boundMulti.c_str()); | ||||||
|  | @ -1839,6 +2046,35 @@ void Socket::UDPConnection::GetDestination(std::string &destIp, uint32_t &port){ | ||||||
|   FAIL_MSG("Could not get destination for UDP socket"); |   FAIL_MSG("Could not get destination for UDP socket"); | ||||||
| }// Socket::UDPConnection GetDestination
 | }// Socket::UDPConnection GetDestination
 | ||||||
| 
 | 
 | ||||||
|  | /// Gets the properties of the receiving end of the local UDP socket.
 | ||||||
|  | /// This will be the sending end for all SendNow calls.
 | ||||||
|  | void Socket::UDPConnection::GetLocalDestination(std::string &destIp, uint32_t &port){ | ||||||
|  |   if (!recvAddr || !recvAddr_size){ | ||||||
|  |     destIp = ""; | ||||||
|  |     port = 0; | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   char addr_str[INET6_ADDRSTRLEN + 1]; | ||||||
|  |   addr_str[INET6_ADDRSTRLEN] = 0; // set last byte to zero, to prevent walking out of the array
 | ||||||
|  |   if (((struct sockaddr_in *)recvAddr)->sin_family == AF_INET6){ | ||||||
|  |     if (inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)recvAddr)->sin6_addr), addr_str, INET6_ADDRSTRLEN) != 0){ | ||||||
|  |       destIp = addr_str; | ||||||
|  |       port = ntohs(((struct sockaddr_in6 *)recvAddr)->sin6_port); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (((struct sockaddr_in *)recvAddr)->sin_family == AF_INET){ | ||||||
|  |     if (inet_ntop(AF_INET, &(((struct sockaddr_in *)recvAddr)->sin_addr), addr_str, INET6_ADDRSTRLEN) != 0){ | ||||||
|  |       destIp = addr_str; | ||||||
|  |       port = ntohs(((struct sockaddr_in *)recvAddr)->sin_port); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   destIp = ""; | ||||||
|  |   port = 0; | ||||||
|  |   FAIL_MSG("Could not get destination for UDP socket"); | ||||||
|  | }// Socket::UDPConnection GetDestination
 | ||||||
|  | 
 | ||||||
| /// Gets the properties of the receiving end of this UDP socket.
 | /// Gets the properties of the receiving end of this UDP socket.
 | ||||||
| /// This will be the receiving end for all SendNow calls.
 | /// This will be the receiving end for all SendNow calls.
 | ||||||
| std::string Socket::UDPConnection::getBinDestination(){ | std::string Socket::UDPConnection::getBinDestination(){ | ||||||
|  | @ -1864,7 +2100,10 @@ uint32_t Socket::UDPConnection::getDestPort() const{ | ||||||
| /// Sets the socket to be blocking if the parameters is true.
 | /// Sets the socket to be blocking if the parameters is true.
 | ||||||
| /// Sets the socket to be non-blocking otherwise.
 | /// Sets the socket to be non-blocking otherwise.
 | ||||||
| void Socket::UDPConnection::setBlocking(bool blocking){ | void Socket::UDPConnection::setBlocking(bool blocking){ | ||||||
|   if (sock >= 0){setFDBlocking(sock, blocking);} |   if (sock >= 0){ | ||||||
|  |     setFDBlocking(sock, blocking); | ||||||
|  |     isBlocking = blocking; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Sends a UDP datagram using the buffer sdata.
 | /// Sends a UDP datagram using the buffer sdata.
 | ||||||
|  | @ -1885,64 +2124,146 @@ void Socket::UDPConnection::SendNow(const char *sdata){ | ||||||
| /// Does not do anything if len < 1.
 | /// Does not do anything if len < 1.
 | ||||||
| /// Prints an DLVL_FAIL level debug message if sending failed.
 | /// Prints an DLVL_FAIL level debug message if sending failed.
 | ||||||
| void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ | void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ | ||||||
|   if (len < 1){return;} |   SendNow(sdata, len, (sockaddr*)destAddr, destAddr_size); | ||||||
|   int r = sendto(sock, sdata, len, 0, (sockaddr *)destAddr, destAddr_size); | } | ||||||
|   if (r > 0){ | 
 | ||||||
|     up += r; | /// Sends a UDP datagram using the buffer sdata of length len.
 | ||||||
|  | /// Does not do anything if len < 1.
 | ||||||
|  | /// Prints an DLVL_FAIL level debug message if sending failed.
 | ||||||
|  | void Socket::UDPConnection::SendNow(const char *sdata, size_t len, sockaddr * dAddr, size_t dAddrLen){ | ||||||
|  |   if (len < 1 || sock == -1){return;} | ||||||
|  |   if (isConnected){ | ||||||
|  |     int r = send(sock, sdata, len, 0); | ||||||
|  |     if (r > 0){ | ||||||
|  |       up += r; | ||||||
|  |     }else{ | ||||||
|  |       if (errno == EDESTADDRREQ){ | ||||||
|  |         close(); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       FAIL_MSG("Could not send UDP data through %d: %s", sock, strerror(errno)); | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (hasReceiveData && recvAddr){ | ||||||
|  |     msghdr mHdr; | ||||||
|  |     char msg_control[0x100]; | ||||||
|  |     iovec iovec; | ||||||
|  |     iovec.iov_base = (void*)sdata; | ||||||
|  |     iovec.iov_len = len; | ||||||
|  |     mHdr.msg_name = dAddr; | ||||||
|  |     mHdr.msg_namelen = dAddrLen; | ||||||
|  |     mHdr.msg_iov = &iovec; | ||||||
|  |     mHdr.msg_iovlen = 1; | ||||||
|  |     mHdr.msg_control = msg_control; | ||||||
|  |     mHdr.msg_controllen = sizeof(msg_control); | ||||||
|  |     mHdr.msg_flags = 0; | ||||||
|  |     int cmsg_space = 0; | ||||||
|  |     cmsghdr * cmsg = CMSG_FIRSTHDR(&mHdr); | ||||||
|  |     cmsg->cmsg_level = IPPROTO_IP; | ||||||
|  |     cmsg->cmsg_type = IP_PKTINFO; | ||||||
|  | 
 | ||||||
|  |     struct in_pktinfo in_pktinfo; | ||||||
|  |     memcpy(&(in_pktinfo.ipi_spec_dst), &(((sockaddr_in*)recvAddr)->sin_family), sizeof(in_pktinfo.ipi_spec_dst)); | ||||||
|  |     in_pktinfo.ipi_ifindex = recvInterface; | ||||||
|  |     cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); | ||||||
|  |     *(struct in_pktinfo*)CMSG_DATA(cmsg) = in_pktinfo; | ||||||
|  |     cmsg_space += CMSG_SPACE(sizeof(in_pktinfo)); | ||||||
|  |     mHdr.msg_controllen = cmsg_space; | ||||||
|  | 
 | ||||||
|  |     int r = sendmsg(sock, &mHdr, 0); | ||||||
|  |     if (r > 0){ | ||||||
|  |       up += r; | ||||||
|  |     }else{ | ||||||
|  |       FAIL_MSG("Could not send UDP data through %d: %s", sock, strerror(errno)); | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|   }else{ |   }else{ | ||||||
|     FAIL_MSG("Could not send UDP data through %d: %s", sock, strerror(errno)); |     int r = sendto(sock, sdata, len, 0, dAddr, dAddrLen); | ||||||
|  |     if (r > 0){ | ||||||
|  |       up += r; | ||||||
|  |     }else{ | ||||||
|  |       FAIL_MSG("Could not send UDP data through %d: %s", sock, strerror(errno)); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Queues sdata, len for sending over this socket.
 | /// Queues sdata, len for sending over this socket.
 | ||||||
| /// If there has been enough time since the last packet, sends immediately.
 | /// If there has been enough time since the last packet, sends immediately.
 | ||||||
| /// Warning: never call sendPaced for the same socket from a different thread!
 | /// Warning: never call sendPaced for the same socket from a different thread!
 | ||||||
| void Socket::UDPConnection::sendPaced(const char *sdata, size_t len){ | /// Note: Only actually encrypts if initDTLS was called in the past.
 | ||||||
|   if (!paceQueue.size() && (!lastPace || Util::getMicros(lastPace) > 10000)){ | void Socket::UDPConnection::sendPaced(const char *sdata, size_t len, bool encrypt){ | ||||||
|     SendNow(sdata, len); |   if (hasDTLS && encrypt){ | ||||||
|     lastPace = Util::getMicros(); |     if (ssl_ctx.state != MBEDTLS_SSL_HANDSHAKE_OVER){ | ||||||
|  |       WARN_MSG("Attempting to write encrypted data before handshake completed! Data was thrown away."); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     int write = mbedtls_ssl_write(&ssl_ctx, (unsigned char*)sdata, len); | ||||||
|  |     if (write <= 0){WARN_MSG("Could not write DTLS packet!");} | ||||||
|   }else{ |   }else{ | ||||||
|     paceQueue.push_back(Util::ResizeablePointer()); |     if (!paceQueue.size() && (!lastPace || Util::getMicros(lastPace) > 10000)){ | ||||||
|     paceQueue.back().assign(sdata, len); |       SendNow(sdata, len); | ||||||
|     // Try to send a packet, if time allows
 |       lastPace = Util::getMicros(); | ||||||
|     //sendPaced(0);
 |     }else{ | ||||||
|  |       paceQueue.push_back(Util::ResizeablePointer()); | ||||||
|  |       paceQueue.back().assign(sdata, len); | ||||||
|  |       // Try to send a packet, if time allows
 | ||||||
|  |       //sendPaced(0);
 | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Gets time in microseconds until next sendPaced call would send something
 | ||||||
|  | size_t Socket::UDPConnection::timeToNextPace(uint64_t uTime){ | ||||||
|  |   size_t qSize = paceQueue.size(); | ||||||
|  |   if (!qSize){return std::string::npos;} // No queue? No time. Return highest possible value.
 | ||||||
|  |   if (!uTime){uTime = Util::getMicros();} | ||||||
|  |   uint64_t paceWait = uTime - lastPace; // Time we've waited so far already
 | ||||||
|  | 
 | ||||||
|  |   // Target clearing the queue in 25ms at most.
 | ||||||
|  |   uint64_t targetTime = 25000 / qSize; | ||||||
|  |   // If this slows us to below 1 packet per 5ms, go that speed instead.
 | ||||||
|  |   if (targetTime > 5000){targetTime = 5000;} | ||||||
|  |   // If the wait is over, send now.
 | ||||||
|  |   if (paceWait >= targetTime){return 0;} | ||||||
|  |   // Return remaining wait time
 | ||||||
|  |   return targetTime - paceWait; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Spends uSendWindow microseconds either sending paced packets or sleeping, whichever is more appropriate
 | /// Spends uSendWindow microseconds either sending paced packets or sleeping, whichever is more appropriate
 | ||||||
| /// Warning: never call sendPaced for the same socket from a different thread!
 | /// Warning: never call sendPaced for the same socket from a different thread!
 | ||||||
| void Socket::UDPConnection::sendPaced(uint64_t uSendWindow){ | void Socket::UDPConnection::sendPaced(uint64_t uSendWindow){ | ||||||
|   uint64_t currPace = Util::getMicros(); |   uint64_t currPace = Util::getMicros(); | ||||||
|  |   uint64_t uTime = currPace; | ||||||
|   do{ |   do{ | ||||||
|     uint64_t uTime = Util::getMicros(); |     uint64_t sleepTime = uSendWindow - (uTime - currPace); | ||||||
|     uint64_t sleepTime = uTime - currPace; |     uint64_t nextPace = timeToNextPace(uTime); | ||||||
|     if (sleepTime > uSendWindow){ |     if (sleepTime > nextPace){sleepTime = nextPace;} | ||||||
|       sleepTime = 0; | 
 | ||||||
|     }else{ |     // Not sleeping? Send now!
 | ||||||
|       sleepTime = uSendWindow - sleepTime; |     if (!sleepTime){ | ||||||
|     } |  | ||||||
|     uint64_t paceWait = uTime - lastPace; |  | ||||||
|     size_t qSize = paceQueue.size(); |  | ||||||
|     // If the queue is complete, wait out the remainder of the time
 |  | ||||||
|     if (!qSize){ |  | ||||||
|       Util::usleep(sleepTime); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     // Otherwise, target clearing the queue in 25ms at most.
 |  | ||||||
|     uint64_t targetTime = 25000 / qSize; |  | ||||||
|     // If this slows us to below 1 packet per 5ms, go that speed instead.
 |  | ||||||
|     if (targetTime > 5000){targetTime = 5000;} |  | ||||||
|     // If the wait is over, send now.
 |  | ||||||
|     if (paceWait >= targetTime){ |  | ||||||
|       SendNow(*paceQueue.begin(), paceQueue.begin()->size()); |       SendNow(*paceQueue.begin(), paceQueue.begin()->size()); | ||||||
|       paceQueue.pop_front(); |       paceQueue.pop_front(); | ||||||
|       lastPace = uTime; |       lastPace = uTime; | ||||||
|       continue; |       continue; | ||||||
|     } |     } | ||||||
|     // Otherwise, wait for the smaller of remaining wait time or remaining send window time.
 | 
 | ||||||
|     if (targetTime - paceWait < sleepTime){sleepTime = targetTime - paceWait;} |     { | ||||||
|     Util::usleep(sleepTime); |       // Use select to wait until a packet arrives or until the next packet should be sent
 | ||||||
|   }while(Util::getMicros(currPace) < uSendWindow); |       fd_set rfds; | ||||||
|  |       struct timeval T; | ||||||
|  |       T.tv_sec = sleepTime / 1000000; | ||||||
|  |       T.tv_usec = sleepTime % 1000000; | ||||||
|  |       // Watch configured FD's for input
 | ||||||
|  |       FD_ZERO(&rfds); | ||||||
|  |       int maxFD = getSock(); | ||||||
|  |       FD_SET(maxFD, &rfds); | ||||||
|  |       int r = select(maxFD + 1, &rfds, NULL, NULL, &T); | ||||||
|  |       // If we can read the socket, immediately return and stop waiting
 | ||||||
|  |       if (r > 0){return;} | ||||||
|  |     } | ||||||
|  |     uTime = Util::getMicros(); | ||||||
|  |   }while(uTime - currPace < uSendWindow); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string Socket::UDPConnection::getBoundAddress(){ | std::string Socket::UDPConnection::getBoundAddress(){ | ||||||
|  | @ -1995,6 +2316,11 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str | ||||||
|   for (rp = addr_result; rp != NULL; rp = rp->ai_next){ |   for (rp = addr_result; rp != NULL; rp = rp->ai_next){ | ||||||
|     sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); |     sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||||||
|     if (sock == -1){continue;} |     if (sock == -1){continue;} | ||||||
|  |     { | ||||||
|  |       // Allow address re-use
 | ||||||
|  |       int on = 1; | ||||||
|  |       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | ||||||
|  |     } | ||||||
|     if (rp->ai_family == AF_INET6){ |     if (rp->ai_family == AF_INET6){ | ||||||
|       const int optval = 0; |       const int optval = 0; | ||||||
|       if (setsockopt(sock, SOL_SOCKET, IPV6_V6ONLY, &optval, sizeof(optval)) < 0){ |       if (setsockopt(sock, SOL_SOCKET, IPV6_V6ONLY, &optval, sizeof(optval)) < 0){ | ||||||
|  | @ -2046,7 +2372,7 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str | ||||||
|       boundAddr = iface; |       boundAddr = iface; | ||||||
|       boundMulti = multicastInterfaces; |       boundMulti = multicastInterfaces; | ||||||
|       boundPort = portNo; |       boundPort = portNo; | ||||||
|       INFO_MSG("UDP bind success on %s:%u (%s)", human_addr, portNo, addrFam(rp->ai_family)); |       INFO_MSG("UDP bind success %d on %s:%u (%s)", sock, human_addr, portNo, addrFam(rp->ai_family)); | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     if (err_str.size()){err_str += ", ";} |     if (err_str.size()){err_str += ", ";} | ||||||
|  | @ -2144,21 +2470,135 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str | ||||||
|   return portNo; |   return portNo; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool Socket::UDPConnection::connect(){ | ||||||
|  |   if (!recvAddr || !recvAddr_size || !destAddr || !destAddr_size){ | ||||||
|  |     WARN_MSG("Attempting to connect a UDP socket without local and/or remote address!"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     std::string destIp; | ||||||
|  |     uint32_t port = 0; | ||||||
|  |     char addr_str[INET6_ADDRSTRLEN + 1]; | ||||||
|  |     if (((struct sockaddr_in *)recvAddr)->sin_family == AF_INET6){ | ||||||
|  |       if (inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)recvAddr)->sin6_addr), addr_str, INET6_ADDRSTRLEN) != 0){ | ||||||
|  |         destIp = addr_str; | ||||||
|  |         port = ntohs(((struct sockaddr_in6 *)recvAddr)->sin6_port); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (((struct sockaddr_in *)recvAddr)->sin_family == AF_INET){ | ||||||
|  |       if (inet_ntop(AF_INET, &(((struct sockaddr_in *)recvAddr)->sin_addr), addr_str, INET6_ADDRSTRLEN) != 0){ | ||||||
|  |         destIp = addr_str; | ||||||
|  |         port = ntohs(((struct sockaddr_in *)recvAddr)->sin_port); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     int ret = ::bind(sock, (const struct sockaddr*)recvAddr, recvAddr_size); | ||||||
|  |     if (!ret){ | ||||||
|  |       INFO_MSG("Bound socket %d to %s:%" PRIu32, sock, destIp.c_str(), port); | ||||||
|  |     }else{ | ||||||
|  |       FAIL_MSG("Failed to bind socket %d (%s) %s:%" PRIu32 ": %s", sock, addrFam(((struct sockaddr_in *)recvAddr)->sin_family), destIp.c_str(), port, strerror(errno)); | ||||||
|  |       std::ofstream bleh("/tmp/socket_recv"); | ||||||
|  |       bleh.write((const char*)recvAddr, recvAddr_size); | ||||||
|  |       bleh.write((const char*)destAddr, destAddr_size); | ||||||
|  |       bleh.close(); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     std::string destIp; | ||||||
|  |     uint32_t port; | ||||||
|  |     char addr_str[INET6_ADDRSTRLEN + 1]; | ||||||
|  |     if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){ | ||||||
|  |       if (inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)destAddr)->sin6_addr), addr_str, INET6_ADDRSTRLEN) != 0){ | ||||||
|  |         destIp = addr_str; | ||||||
|  |         port = ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET){ | ||||||
|  |       if (inet_ntop(AF_INET, &(((struct sockaddr_in *)destAddr)->sin_addr), addr_str, INET6_ADDRSTRLEN) != 0){ | ||||||
|  |         destIp = addr_str; | ||||||
|  |         port = ntohs(((struct sockaddr_in *)destAddr)->sin_port); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     int ret = ::connect(sock, (const struct sockaddr*)destAddr, destAddr_size); | ||||||
|  |     if (!ret){ | ||||||
|  |       INFO_MSG("Connected socket to %s:%" PRIu32, destIp.c_str(), port); | ||||||
|  |     }else{ | ||||||
|  |       FAIL_MSG("Failed to connect socket to %s:%" PRIu32 ": %s", destIp.c_str(), port, strerror(errno)); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   isConnected = true; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /// Attempt to receive a UDP packet.
 | /// Attempt to receive a UDP packet.
 | ||||||
| /// This will automatically allocate or resize the internal data buffer if needed.
 | /// This will automatically allocate or resize the internal data buffer if needed.
 | ||||||
| /// If a packet is received, it will be placed in the "data" member, with it's length in "data_len".
 | /// If a packet is received, it will be placed in the "data" member, with it's length in "data_len".
 | ||||||
| /// \return True if a packet was received, false otherwise.
 | /// \return True if a packet was received, false otherwise.
 | ||||||
| bool Socket::UDPConnection::Receive(){ | bool Socket::UDPConnection::Receive(){ | ||||||
|  |   if (pretendReceive){ | ||||||
|  |     pretendReceive = false; | ||||||
|  |     return onData(); | ||||||
|  |   } | ||||||
|   if (sock == -1){return false;} |   if (sock == -1){return false;} | ||||||
|   data.truncate(0); |   data.truncate(0); | ||||||
|  |   if (isConnected){ | ||||||
|  |     int r = recv(sock, data, data.rsize(), MSG_TRUNC | MSG_DONTWAIT); | ||||||
|  |     if (r == -1){ | ||||||
|  |       if (errno != EAGAIN){ | ||||||
|  |         INFO_MSG("UDP receive: %d (%s)", errno, strerror(errno)); | ||||||
|  |         if (errno == ECONNREFUSED){close();} | ||||||
|  |       } | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     if (r > 0){ | ||||||
|  |       data.append(0, r); | ||||||
|  |       down += r; | ||||||
|  |       if (data.rsize() < (unsigned int)r){ | ||||||
|  |         INFO_MSG("Doubling UDP socket buffer from %" PRIu32 " to %" PRIu32, data.rsize(), data.rsize()*2); | ||||||
|  |         data.allocate(data.rsize()*2); | ||||||
|  |       } | ||||||
|  |       return onData(); | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|   sockaddr_in6 addr; |   sockaddr_in6 addr; | ||||||
|   socklen_t destsize = sizeof(addr); |   socklen_t destsize = sizeof(addr); | ||||||
|   int r = recvfrom(sock, data, data.rsize(), MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&addr, &destsize); |   //int r = recvfrom(sock, data, data.rsize(), MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&addr, &destsize);
 | ||||||
|  |   msghdr mHdr; | ||||||
|  |   memset(&mHdr, 0, sizeof(mHdr)); | ||||||
|  |   char ctrl[0x100]; | ||||||
|  |   iovec dBufs; | ||||||
|  |   dBufs.iov_base = data; | ||||||
|  |   dBufs.iov_len = data.rsize(); | ||||||
|  |   mHdr.msg_name = &addr; | ||||||
|  |   mHdr.msg_namelen = destsize; | ||||||
|  |   mHdr.msg_control = ctrl; | ||||||
|  |   mHdr.msg_controllen = 0x100; | ||||||
|  |   mHdr.msg_iov = &dBufs; | ||||||
|  |   mHdr.msg_iovlen = 1; | ||||||
|  |   int r = recvmsg(sock, &mHdr, MSG_TRUNC | MSG_DONTWAIT); | ||||||
|  |   destsize = mHdr.msg_namelen; | ||||||
|   if (r == -1){ |   if (r == -1){ | ||||||
|     if (errno != EAGAIN){INFO_MSG("UDP receive: %d (%s)", errno, strerror(errno));} |     if (errno != EAGAIN){INFO_MSG("UDP receive: %d (%s)", errno, strerror(errno));} | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   if (destAddr && destsize && destAddr_size >= destsize){memcpy(destAddr, &addr, destsize);} |   if (destAddr && destsize && destAddr_size >= destsize){memcpy(destAddr, &addr, destsize);} | ||||||
|  |   if (recvAddr){ | ||||||
|  |     for ( struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mHdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&mHdr, cmsg)){ | ||||||
|  |       if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO){continue;} | ||||||
|  |       struct in_pktinfo* pi = (in_pktinfo*)CMSG_DATA(cmsg); | ||||||
|  |       struct sockaddr_in * recvCast = (sockaddr_in*)recvAddr; | ||||||
|  |       recvCast->sin_family = family; | ||||||
|  |       recvCast->sin_port = htons(boundPort); | ||||||
|  |       memcpy(&(recvCast->sin_addr), &(pi->ipi_spec_dst), sizeof(pi->ipi_spec_dst)); | ||||||
|  |       recvInterface = pi->ipi_ifindex; | ||||||
|  |       hasReceiveData = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|   data.append(0, r); |   data.append(0, r); | ||||||
|   down += r; |   down += r; | ||||||
|   //Handle UDP packets that are too large
 |   //Handle UDP packets that are too large
 | ||||||
|  | @ -2166,7 +2606,88 @@ bool Socket::UDPConnection::Receive(){ | ||||||
|     INFO_MSG("Doubling UDP socket buffer from %" PRIu32 " to %" PRIu32, data.rsize(), data.rsize()*2); |     INFO_MSG("Doubling UDP socket buffer from %" PRIu32 " to %" PRIu32, data.rsize(), data.rsize()*2); | ||||||
|     data.allocate(data.rsize()*2); |     data.allocate(data.rsize()*2); | ||||||
|   } |   } | ||||||
|   return (r > 0); |   return onData(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Socket::UDPConnection::onData(){ | ||||||
|  |   wasEncrypted = false; | ||||||
|  |   if (!data.size()){return false;} | ||||||
|  |   uint8_t fb = 0; | ||||||
|  |   int r = data.size(); | ||||||
|  |   if (r){fb = (uint8_t)data[0];} | ||||||
|  |   if (r && hasDTLS && fb > 19 && fb < 64){ | ||||||
|  |     if (nextDTLSReadLen){ | ||||||
|  |       INFO_MSG("Overwriting %zu bytes of unread dTLS data!", nextDTLSReadLen); | ||||||
|  |     } | ||||||
|  |     nextDTLSRead = data; | ||||||
|  |     nextDTLSReadLen = data.size(); | ||||||
|  |     // Complete dTLS handshake if needed
 | ||||||
|  |     if (ssl_ctx.state != MBEDTLS_SSL_HANDSHAKE_OVER){ | ||||||
|  |       do{ | ||||||
|  |         r = mbedtls_ssl_handshake(&ssl_ctx); | ||||||
|  |         switch (r){ | ||||||
|  |         case 0:{ // Handshake complete
 | ||||||
|  |           INFO_MSG("dTLS handshake complete!"); | ||||||
|  |           int extrRes = 0; | ||||||
|  |           uint8_t keying_material[MBEDTLS_DTLS_SRTP_MAX_KEY_MATERIAL_LENGTH]; | ||||||
|  |           size_t keying_material_len = sizeof(keying_material); | ||||||
|  |           extrRes = mbedtls_ssl_get_dtls_srtp_key_material(&ssl_ctx, keying_material, &keying_material_len); | ||||||
|  |           if (extrRes){ | ||||||
|  |             char mbedtls_msg[1024]; | ||||||
|  |             mbedtls_strerror(extrRes, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |             WARN_MSG("dTLS could not extract keying material: %s", mbedtls_msg); | ||||||
|  |             return Receive(); | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           mbedtls_ssl_srtp_profile srtp_profile = mbedtls_ssl_get_dtls_srtp_protection_profile(&ssl_ctx); | ||||||
|  |           switch (srtp_profile){ | ||||||
|  |           case MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_80:{ | ||||||
|  |             cipher = "SRTP_AES128_CM_SHA1_80"; | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |           case MBEDTLS_SRTP_AES128_CM_HMAC_SHA1_32:{ | ||||||
|  |             cipher = "SRTP_AES128_CM_SHA1_32"; | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |           default:{ | ||||||
|  |             WARN_MSG("Unhandled SRTP profile, cannot extract keying material."); | ||||||
|  |             return Receive(); | ||||||
|  |           } | ||||||
|  |           } | ||||||
|  |           remote_key.assign((char *)(&keying_material[0]) + 0, 16); | ||||||
|  |           local_key.assign((char *)(&keying_material[0]) + 16, 16); | ||||||
|  |           remote_salt.assign((char *)(&keying_material[0]) + 32, 14); | ||||||
|  |           local_salt.assign((char *)(&keying_material[0]) + 46, 14); | ||||||
|  |           return Receive(); // No application-level data to read
 | ||||||
|  |         } | ||||||
|  |         case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED:{ | ||||||
|  |           dTLSReset(); | ||||||
|  |           return Receive(); // No application-level data to read
 | ||||||
|  |         } | ||||||
|  |         case MBEDTLS_ERR_SSL_WANT_READ:{ | ||||||
|  |           return Receive(); // No application-level data to read
 | ||||||
|  |         } | ||||||
|  |         default:{ | ||||||
|  |           char mbedtls_msg[1024]; | ||||||
|  |           mbedtls_strerror(r, mbedtls_msg, sizeof(mbedtls_msg)); | ||||||
|  |           WARN_MSG("dTLS could not handshake: %s", mbedtls_msg); | ||||||
|  |           return Receive(); // No application-level data to read
 | ||||||
|  |         } | ||||||
|  |         } | ||||||
|  |       }while (r == MBEDTLS_ERR_SSL_WANT_WRITE); | ||||||
|  |     }else{ | ||||||
|  |       int read = mbedtls_ssl_read(&ssl_ctx, (unsigned char *)(char*)data, data.size()); | ||||||
|  |       if (read <= 0){ | ||||||
|  |         // Non-encrypted read (encrypted read fail)
 | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       // Encrypted read success
 | ||||||
|  |       wasEncrypted = true; | ||||||
|  |       data.truncate(read); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return r > 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int Socket::UDPConnection::getSock(){ | int Socket::UDPConnection::getSock(){ | ||||||
|  |  | ||||||
							
								
								
									
										51
									
								
								lib/socket.h
									
										
									
									
									
								
							
							
						
						
									
										51
									
								
								lib/socket.h
									
										
									
									
									
								
							|  | @ -18,12 +18,14 @@ | ||||||
| #include "util.h" | #include "util.h" | ||||||
| 
 | 
 | ||||||
| #ifdef SSL | #ifdef SSL | ||||||
| #include "mbedtls/ctr_drbg.h" | #include <mbedtls/ctr_drbg.h> | ||||||
| #include "mbedtls/debug.h" | #include <mbedtls/debug.h> | ||||||
| #include "mbedtls/entropy.h" | #include <mbedtls/entropy.h> | ||||||
| #include "mbedtls/error.h" | #include <mbedtls/error.h> | ||||||
| #include "mbedtls/net.h" | #include <mbedtls/net.h> | ||||||
| #include "mbedtls/ssl.h" | #include <mbedtls/ssl.h> | ||||||
|  | #include <mbedtls/ssl_cookie.h> | ||||||
|  | #include <mbedtls/timing.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  | @ -196,10 +198,13 @@ namespace Socket{ | ||||||
| 
 | 
 | ||||||
|   class UDPConnection{ |   class UDPConnection{ | ||||||
|   private: |   private: | ||||||
|  |     void init(bool nonblock, int family = AF_INET6); | ||||||
|     int sock;                   ///< Internally saved socket number.
 |     int sock;                   ///< Internally saved socket number.
 | ||||||
|     std::string remotehost;     ///< Stores remote host address
 |     std::string remotehost;     ///< Stores remote host address
 | ||||||
|     void *destAddr;             ///< Destination address pointer.
 |     void *destAddr;             ///< Destination address pointer.
 | ||||||
|     unsigned int destAddr_size; ///< Size of the destination address pointer.
 |     unsigned int destAddr_size; ///< Size of the destination address pointer.
 | ||||||
|  |     void *recvAddr;             ///< Destination address pointer.
 | ||||||
|  |     unsigned int recvAddr_size; ///< Size of the destination address pointer.
 | ||||||
|     unsigned int up;            ///< Amount of bytes transferred up.
 |     unsigned int up;            ///< Amount of bytes transferred up.
 | ||||||
|     unsigned int down;          ///< Amount of bytes transferred down.
 |     unsigned int down;          ///< Amount of bytes transferred down.
 | ||||||
|     int family;                 ///< Current socket address family
 |     int family;                 ///< Current socket address family
 | ||||||
|  | @ -208,19 +213,46 @@ namespace Socket{ | ||||||
|     void checkRecvBuf(); |     void checkRecvBuf(); | ||||||
|     std::deque<Util::ResizeablePointer> paceQueue; |     std::deque<Util::ResizeablePointer> paceQueue; | ||||||
|     uint64_t lastPace; |     uint64_t lastPace; | ||||||
|  |     int recvInterface; | ||||||
|  |     bool hasReceiveData; | ||||||
|  |     bool isBlocking; | ||||||
|  |     bool isConnected; | ||||||
|  |     bool pretendReceive; ///< If true, will pretend to have just received the current data buffer on new Receive() call
 | ||||||
|  |     bool onData(); | ||||||
|  |     | ||||||
|  |     // dTLS-related members
 | ||||||
|  |     bool hasDTLS; ///< True if dTLS is enabled
 | ||||||
|  |     void * nextDTLSRead; | ||||||
|  |     size_t nextDTLSReadLen; | ||||||
|  |     mbedtls_entropy_context entropy_ctx; | ||||||
|  |     mbedtls_ctr_drbg_context rand_ctx; | ||||||
|  |     mbedtls_ssl_context ssl_ctx; | ||||||
|  |     mbedtls_ssl_config ssl_conf; | ||||||
|  |     mbedtls_ssl_cookie_ctx cookie_ctx; | ||||||
|  |     mbedtls_timing_delay_context timer_ctx; | ||||||
| 
 | 
 | ||||||
|   public: |   public: | ||||||
|     Util::ResizeablePointer data; |     Util::ResizeablePointer data; | ||||||
|     UDPConnection(const UDPConnection &o); |     UDPConnection(const UDPConnection &o); | ||||||
|     UDPConnection(bool nonblock = false); |     UDPConnection(bool nonblock = false); | ||||||
|     ~UDPConnection(); |     ~UDPConnection(); | ||||||
|  |     bool operator==(const UDPConnection& b) const; | ||||||
|  |     operator bool() const; | ||||||
|  |     void initDTLS(mbedtls_x509_crt *cert, mbedtls_pk_context *key); | ||||||
|  |     void deinitDTLS(); | ||||||
|  |     int dTLSRead(unsigned char *buf, size_t len); | ||||||
|  |     int dTLSWrite(const unsigned char *buf, size_t len); | ||||||
|  |     void dTLSReset(); | ||||||
|  |     bool wasEncrypted; | ||||||
|     void close(); |     void close(); | ||||||
|     int getSock(); |     int getSock(); | ||||||
|     uint16_t bind(int port, std::string iface = "", const std::string &multicastAddress = ""); |     uint16_t bind(int port, std::string iface = "", const std::string &multicastAddress = ""); | ||||||
|  |     bool connect(); | ||||||
|     void setBlocking(bool blocking); |     void setBlocking(bool blocking); | ||||||
|     void allocateDestination(); |     void allocateDestination(); | ||||||
|     void SetDestination(std::string hostname, uint32_t port); |     void SetDestination(std::string hostname, uint32_t port); | ||||||
|     void GetDestination(std::string &hostname, uint32_t &port); |     void GetDestination(std::string &hostname, uint32_t &port); | ||||||
|  |     void GetLocalDestination(std::string &hostname, uint32_t &port); | ||||||
|     std::string getBinDestination(); |     std::string getBinDestination(); | ||||||
|     const void * getDestAddr(){return destAddr;} |     const void * getDestAddr(){return destAddr;} | ||||||
|     size_t getDestAddrLen(){return destAddr_size;} |     size_t getDestAddrLen(){return destAddr_size;} | ||||||
|  | @ -230,8 +262,13 @@ namespace Socket{ | ||||||
|     void SendNow(const std::string &data); |     void SendNow(const std::string &data); | ||||||
|     void SendNow(const char *data); |     void SendNow(const char *data); | ||||||
|     void SendNow(const char *data, size_t len); |     void SendNow(const char *data, size_t len); | ||||||
|     void sendPaced(const char * data, size_t len); |     void SendNow(const char *sdata, size_t len, sockaddr * dAddr, size_t dAddrLen); | ||||||
|  |     void sendPaced(const char * data, size_t len, bool encrypt = true); | ||||||
|     void sendPaced(uint64_t uSendWindow); |     void sendPaced(uint64_t uSendWindow); | ||||||
|  |     size_t timeToNextPace(uint64_t uTime = 0); | ||||||
|     void setSocketFamily(int AF_TYPE); |     void setSocketFamily(int AF_TYPE); | ||||||
|  | 
 | ||||||
|  |     // dTLS-related public members
 | ||||||
|  |     std::string cipher, remote_key, local_key, remote_salt, local_salt; | ||||||
|   }; |   }; | ||||||
| }// namespace Socket
 | }// namespace Socket
 | ||||||
|  |  | ||||||
|  | @ -129,6 +129,8 @@ if usessl | ||||||
| 
 | 
 | ||||||
|   mist_deps += [mbedtls, mbedx509, mbedcrypto] |   mist_deps += [mbedtls, mbedx509, mbedcrypto] | ||||||
|   mist_deps += dependency('libsrtp2', default_options: ['tests=disabled'], fallback: ['libsrtp2', 'libsrtp2_dep']) |   mist_deps += dependency('libsrtp2', default_options: ['tests=disabled'], fallback: ['libsrtp2', 'libsrtp2_dep']) | ||||||
|  | 
 | ||||||
|  |   usrsctp_dep = dependency('usrsctp', fallback: ['usrsctp', 'usrsctp_dep']) | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| libsrt = false | libsrt = false | ||||||
|  |  | ||||||
|  | @ -91,6 +91,7 @@ foreach output : outputs | ||||||
|     endif |     endif | ||||||
|     if extra.contains('srtp') |     if extra.contains('srtp') | ||||||
|       sources += files('output_webrtc_srtp.cpp', 'output_webrtc_srtp.h') |       sources += files('output_webrtc_srtp.cpp', 'output_webrtc_srtp.h') | ||||||
|  |       deps += usrsctp_dep | ||||||
|     endif |     endif | ||||||
|     if extra.contains('embed') |     if extra.contains('embed') | ||||||
|       sources += embed_tgts |       sources += embed_tgts | ||||||
|  |  | ||||||
|  | @ -735,18 +735,22 @@ namespace Mist{ | ||||||
|     std::set<size_t> validTracks = M.getValidTracks(); |     std::set<size_t> validTracks = M.getValidTracks(); | ||||||
|     if (!validTracks.size()){return 0;} |     if (!validTracks.size()){return 0;} | ||||||
|     uint64_t start = 0xFFFFFFFFFFFFFFFFull; |     uint64_t start = 0xFFFFFFFFFFFFFFFFull; | ||||||
|  |     uint64_t nonMetaStart = 0xFFFFFFFFFFFFFFFFull; | ||||||
|     if (userSelect.size()){ |     if (userSelect.size()){ | ||||||
|       for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){ |       for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){ | ||||||
|         if (M.trackValid(it->first) && start > M.getFirstms(it->first)){ |         if (M.trackValid(it->first) && start > M.getFirstms(it->first)){ | ||||||
|           start = M.getFirstms(it->first); |           start = M.getFirstms(it->first); | ||||||
|         } |         } | ||||||
|  |         if (M.trackValid(it->first) && M.getType(it->first) != "meta" && nonMetaStart > M.getFirstms(it->first)){ | ||||||
|  |           nonMetaStart = M.getFirstms(it->first); | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     }else{ |     }else{ | ||||||
|       for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ |       for (std::set<size_t>::iterator it = validTracks.begin(); it != validTracks.end(); it++){ | ||||||
|         if (start > M.getFirstms(*it)){start = M.getFirstms(*it);} |         if (start > M.getFirstms(*it)){start = M.getFirstms(*it);} | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     return start; |     return nonMetaStart != 0xFFFFFFFFFFFFFFFFull ? nonMetaStart: start; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Return the end time of the selected tracks, or 0 if unknown or live.
 |   /// Return the end time of the selected tracks, or 0 if unknown or live.
 | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,7 +3,6 @@ | ||||||
| #include "output.h" | #include "output.h" | ||||||
| #include "output_http.h" | #include "output_http.h" | ||||||
| #include <mist/certificate.h> | #include <mist/certificate.h> | ||||||
| #include <mist/dtls_srtp_handshake.h> |  | ||||||
| #include <mist/h264.h> | #include <mist/h264.h> | ||||||
| #include <mist/http_parser.h> | #include <mist/http_parser.h> | ||||||
| #include <mist/rtp_fec.h> | #include <mist/rtp_fec.h> | ||||||
|  | @ -14,6 +13,7 @@ | ||||||
| #include <mist/websocket.h> | #include <mist/websocket.h> | ||||||
| #include <fstream> | #include <fstream> | ||||||
| #include "output_webrtc_srtp.h" | #include "output_webrtc_srtp.h" | ||||||
|  | #include <usrsctp.h> | ||||||
| 
 | 
 | ||||||
| #define NACK_BUFFER_SIZE 1024 | #define NACK_BUFFER_SIZE 1024 | ||||||
| 
 | 
 | ||||||
|  | @ -67,7 +67,19 @@ namespace Mist{ | ||||||
|     double jitter; |     double jitter; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   /* ------------------------------------------------ */ |   class WebRTCSocket{ | ||||||
|  |   public: | ||||||
|  |     WebRTCSocket(); | ||||||
|  |     Socket::UDPConnection* udpSock; | ||||||
|  |     SRTPReader srtpReader; ///< Used to unprotect incoming RTP and RTCP data. Uses the keys that
 | ||||||
|  |                            ///< were exchanged with DTLS.
 | ||||||
|  |     SRTPWriter srtpWriter; ///< Used to protect our RTP and RTCP data when sending data to another
 | ||||||
|  |                            ///< peer. Uses the keys that were exchanged with DTLS.
 | ||||||
|  |     std::map<uint32_t, nackBuffer> outBuffers; | ||||||
|  |     size_t sendRTCP(const char * data, size_t len); | ||||||
|  |     size_t ackNACK(uint32_t pSSRC, uint16_t seq); | ||||||
|  |     Util::ResizeablePointer dataBuffer; | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   class OutWebRTC : public HTTPOutput{ |   class OutWebRTC : public HTTPOutput{ | ||||||
|   public: |   public: | ||||||
|  | @ -82,10 +94,13 @@ namespace Mist{ | ||||||
|     virtual bool dropPushTrack(uint32_t trackId, const std::string & dropReason); |     virtual bool dropPushTrack(uint32_t trackId, const std::string & dropReason); | ||||||
|     void handleWebsocketIdle(); |     void handleWebsocketIdle(); | ||||||
|     virtual void onFail(const std::string &msg, bool critical = false); |     virtual void onFail(const std::string &msg, bool critical = false); | ||||||
|     bool onFinish(); |  | ||||||
|     bool doesWebsockets(){return true;} |     bool doesWebsockets(){return true;} | ||||||
|     void handleWebRTCInputOutputFromThread(); |     void handleWebRTCInputOutputFromThread(); | ||||||
|     int onDTLSHandshakeWantsToWrite(const uint8_t *data, int *nbytes); |     bool handleUDPSocket(Socket::UDPConnection & sock); | ||||||
|  |     bool handleUDPSocket(WebRTCSocket & wSock); | ||||||
|  |     void sendSCTPPacket(const char * data, size_t len); | ||||||
|  |     void sendPaced(uint64_t uSendWindow); | ||||||
|  |     void onSCTP(const char * data, size_t len, uint16_t stream, uint32_t ppid); | ||||||
|     void onRTPSorterHasPacket(size_t tid, const RTP::Packet &pkt); |     void onRTPSorterHasPacket(size_t tid, const RTP::Packet &pkt); | ||||||
|     void onDTSCConverterHasPacket(const DTSC::Packet &pkt); |     void onDTSCConverterHasPacket(const DTSC::Packet &pkt); | ||||||
|     void onDTSCConverterHasInitData(const size_t trackID, const std::string &initData); |     void onDTSCConverterHasInitData(const size_t trackID, const std::string &initData); | ||||||
|  | @ -95,7 +110,7 @@ namespace Mist{ | ||||||
|     inline virtual bool keepGoing(){return config->is_active && (noSignalling || myConn);} |     inline virtual bool keepGoing(){return config->is_active && (noSignalling || myConn);} | ||||||
|     virtual void requestHandler(); |     virtual void requestHandler(); | ||||||
|   protected: |   protected: | ||||||
|     virtual void idleTime(uint64_t ms){udp.sendPaced(ms*1000);} |     virtual void idleTime(uint64_t ms){sendPaced(ms*1000);} | ||||||
|   private: |   private: | ||||||
|     bool noSignalling; |     bool noSignalling; | ||||||
|     uint64_t lastRecv; |     uint64_t lastRecv; | ||||||
|  | @ -109,9 +124,8 @@ namespace Mist{ | ||||||
|     void ackNACK(uint32_t SSRC, uint16_t seq); |     void ackNACK(uint32_t SSRC, uint16_t seq); | ||||||
|     bool handleWebRTCInputOutput(); ///< Reads data from the UDP socket. Returns true when we read
 |     bool handleWebRTCInputOutput(); ///< Reads data from the UDP socket. Returns true when we read
 | ||||||
|                                     ///< some data, othewise false.
 |                                     ///< some data, othewise false.
 | ||||||
|     void handleReceivedSTUNPacket(); |     void handleReceivedSTUNPacket(WebRTCSocket &wSock); | ||||||
|     void handleReceivedDTLSPacket(); |     void handleReceivedRTPOrRTCPPacket(WebRTCSocket &wSock); | ||||||
|     void handleReceivedRTPOrRTCPPacket(); |  | ||||||
|     bool handleSignalingCommandRemoteOfferForInput(SDP::Session &sdpSession); |     bool handleSignalingCommandRemoteOfferForInput(SDP::Session &sdpSession); | ||||||
|     bool handleSignalingCommandRemoteOfferForOutput(SDP::Session &sdpSession); |     bool handleSignalingCommandRemoteOfferForOutput(SDP::Session &sdpSession); | ||||||
|     void sendSignalingError(const std::string &commandType, const std::string &errorMessage); |     void sendSignalingError(const std::string &commandType, const std::string &errorMessage); | ||||||
|  | @ -136,20 +150,19 @@ namespace Mist{ | ||||||
|     SDP::Session sdp;      ///< SDP parser.
 |     SDP::Session sdp;      ///< SDP parser.
 | ||||||
|     SDP::Answer sdpAnswer; ///< WIP: Replacing our `sdp` member ..
 |     SDP::Answer sdpAnswer; ///< WIP: Replacing our `sdp` member ..
 | ||||||
|     Certificate cert;      ///< The TLS certificate. Used to generate a fingerprint in SDP answers.
 |     Certificate cert;      ///< The TLS certificate. Used to generate a fingerprint in SDP answers.
 | ||||||
|     DTLSSRTPHandshake dtlsHandshake; ///< Implements the DTLS handshake using the mbedtls library (fork).
 |     Socket::UDPConnection mainSocket; //< Main socket created during the initial handshake
 | ||||||
|     SRTPReader srtpReader; ///< Used to unprotect incoming RTP and RTCP data. Uses the keys that
 |     std::map<int, WebRTCSocket> sockets; ///< UDP sockets over which WebRTC data is received and sent.
 | ||||||
|                            ///< were exchanged with DTLS.
 |     std::set<int> rtpSockets; ///< UDP sockets over which (S)RTP data is transmitted/received
 | ||||||
|     SRTPWriter srtpWriter; ///< Used to protect our RTP and RTCP data when sending data to another
 |     std::set<int> sctpSockets; ///< UDP sockets over which (S)RTP data is transmitted/received
 | ||||||
|                            ///< peer. Uses the keys that were exchanged with DTLS.
 |     uint16_t lastMediaSocket; //< Last socket number we received video/audio on
 | ||||||
|     Socket::UDPConnection udp; ///< Our UDP socket over which WebRTC data is received and sent.
 |     uint16_t lastMetaSocket; //< Last socket number we received non-media data on
 | ||||||
|  |     uint16_t udpPort; ///< Port where we receive RTP, STUN, DTLS, etc.
 | ||||||
|     StunReader stunReader;     ///< Decodes STUN messages; during a session we keep receiving STUN
 |     StunReader stunReader;     ///< Decodes STUN messages; during a session we keep receiving STUN
 | ||||||
|                                ///< messages to which we need to reply.
 |                                ///< messages to which we need to reply.
 | ||||||
|     std::map<uint64_t, WebRTCTrack> webrtcTracks; ///< WebRTCTracks indexed by payload type for incoming data and indexed by
 |     std::map<uint64_t, WebRTCTrack> webrtcTracks; ///< WebRTCTracks indexed by payload type for incoming data and indexed by
 | ||||||
|                                                   ///< myMeta.tracks[].trackID for outgoing data.
 |                                                   ///< myMeta.tracks[].trackID for outgoing data.
 | ||||||
|     tthread::thread *webRTCInputOutputThread; ///< The thread in which we read WebRTC data when
 |     tthread::thread *webRTCInputOutputThread; ///< The thread in which we read WebRTC data when
 | ||||||
|                                               ///< we're receive media from another peer.
 |                                               ///< we're receive media from another peer.
 | ||||||
|     uint16_t udpPort; ///< The port on which our webrtc socket is bound. This is where we receive
 |  | ||||||
|                       ///< RTP, STUN, DTLS, etc. */
 |  | ||||||
|     uint32_t SSRC; ///< The SSRC for this local instance. Is used when generating RTCP reports. */
 |     uint32_t SSRC; ///< The SSRC for this local instance. Is used when generating RTCP reports. */
 | ||||||
|     uint64_t rtcpTimeoutInMillis; ///< When current time in millis exceeds this timeout we have to
 |     uint64_t rtcpTimeoutInMillis; ///< When current time in millis exceeds this timeout we have to
 | ||||||
|                                   ///< send a new RTCP packet.
 |                                   ///< send a new RTCP packet.
 | ||||||
|  | @ -161,18 +174,15 @@ namespace Mist{ | ||||||
|                            ///< the signaling channel. Defaults to 6mbit.
 |                            ///< the signaling channel. Defaults to 6mbit.
 | ||||||
|     uint32_t videoConstraint; |     uint32_t videoConstraint; | ||||||
| 
 | 
 | ||||||
|     size_t audTrack, vidTrack, prevVidTrack; |     size_t audTrack, vidTrack, prevVidTrack, metaTrack; | ||||||
|     double target_rate; ///< Target playback speed rate (1.0 = normal, 0 = auto)
 |     double target_rate; ///< Target playback speed rate (1.0 = normal, 0 = auto)
 | ||||||
| 
 | 
 | ||||||
|     bool didReceiveKeyFrame; /* TODO burst delay */ |     bool didReceiveKeyFrame; | ||||||
|     bool setPacketOffset; |     bool setPacketOffset; | ||||||
|     int64_t packetOffset;    ///< For timestamp rewrite with BMO
 |     int64_t packetOffset;    ///< For timestamp rewrite with BMO
 | ||||||
|     uint64_t lastTimeSync; |     uint64_t lastTimeSync; | ||||||
|     bool firstKey; |     bool firstKey; | ||||||
|     bool repeatInit; |     bool repeatInit; | ||||||
|     bool stayLive; |  | ||||||
|     bool doDTLS; |  | ||||||
|     bool volkswagenMode; |  | ||||||
| 
 | 
 | ||||||
|     double stats_jitter; |     double stats_jitter; | ||||||
|     uint64_t stats_nacknum; |     uint64_t stats_nacknum; | ||||||
|  | @ -191,13 +201,17 @@ namespace Mist{ | ||||||
|     std::map<uint8_t, uint64_t> payloadTypeToWebRTCTrack; ///< Maps e.g. RED to the corresponding track. Used when input
 |     std::map<uint8_t, uint64_t> payloadTypeToWebRTCTrack; ///< Maps e.g. RED to the corresponding track. Used when input
 | ||||||
|                                                           ///< supports RED/ULPFEC; can also be used to map RTX in the
 |                                                           ///< supports RED/ULPFEC; can also be used to map RTX in the
 | ||||||
|                                                           ///< future.
 |                                                           ///< future.
 | ||||||
|     std::map<uint32_t, nackBuffer> outBuffers; |  | ||||||
| 
 |  | ||||||
|     uint64_t lastSR; |     uint64_t lastSR; | ||||||
|     std::set<size_t> mustSendSR; |     std::set<size_t> mustSendSR; | ||||||
| 
 | 
 | ||||||
|     int64_t ntpClockDifference; |     int64_t ntpClockDifference; | ||||||
|     bool syncedNTPClock; |     bool syncedNTPClock; | ||||||
|  | 
 | ||||||
|  |     bool sctpInited; | ||||||
|  |     bool sctpConnected; | ||||||
|  |     struct socket * sctp_sock; | ||||||
|  |     std::map<std::string, uint16_t> dataChannels; | ||||||
|  |     std::deque<std::string> queuedJSON; | ||||||
|   }; |   }; | ||||||
| }// namespace Mist
 | }// namespace Mist
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,6 +14,10 @@ SRTPReader::SRTPReader(){ | ||||||
|   memset((void *)&policy, 0x00, sizeof(policy)); |   memset((void *)&policy, 0x00, sizeof(policy)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SRTPReader::~SRTPReader(){ | ||||||
|  |   if (shutdown() != 0){FAIL_MSG("Failed to cleanly shutdown the srtp reader.");} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|   Before initializing the srtp library we shut it down first |   Before initializing the srtp library we shut it down first | ||||||
|   because initializing the library twice results in an error. |   because initializing the library twice results in an error. | ||||||
|  | @ -203,6 +207,11 @@ SRTPWriter::SRTPWriter(){ | ||||||
|   memset((void *)&policy, 0x00, sizeof(policy)); |   memset((void *)&policy, 0x00, sizeof(policy)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SRTPWriter::~SRTPWriter(){ | ||||||
|  |   if (shutdown() != 0){FAIL_MSG("Failed to cleanly shutdown the srtp writer.");} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|   Before initializing the srtp library we shut it down first |   Before initializing the srtp library we shut it down first | ||||||
|   because initializing the library twice results in an error. |   because initializing the library twice results in an error. | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| class SRTPReader{ | class SRTPReader{ | ||||||
| public: | public: | ||||||
|   SRTPReader(); |   SRTPReader(); | ||||||
|  |   ~SRTPReader(); | ||||||
|   int init(const std::string &cipher, const std::string &key, const std::string &salt); |   int init(const std::string &cipher, const std::string &key, const std::string &salt); | ||||||
|   int shutdown(); |   int shutdown(); | ||||||
|   int unprotectRtp(uint8_t *data, int *nbytes); /* `nbytes` should contain the number of bytes in `data`. On success `nbytes`
 |   int unprotectRtp(uint8_t *data, int *nbytes); /* `nbytes` should contain the number of bytes in `data`. On success `nbytes`
 | ||||||
|  | @ -32,6 +33,7 @@ private: | ||||||
| class SRTPWriter{ | class SRTPWriter{ | ||||||
| public: | public: | ||||||
|   SRTPWriter(); |   SRTPWriter(); | ||||||
|  |   ~SRTPWriter(); | ||||||
|   int init(const std::string &cipher, const std::string &key, const std::string &salt); |   int init(const std::string &cipher, const std::string &key, const std::string &salt); | ||||||
|   int shutdown(); |   int shutdown(); | ||||||
|   int protectRtp(uint8_t *data, int *nbytes); |   int protectRtp(uint8_t *data, int *nbytes); | ||||||
|  |  | ||||||
|  | @ -67,23 +67,23 @@ void userOnActive(Comms::Connections &connections, size_t idx){ | ||||||
|   } |   } | ||||||
|   // Sanity checks
 |   // Sanity checks
 | ||||||
|   if (connections.getDown(idx) < connDown[idx]){ |   if (connections.getDown(idx) < connDown[idx]){ | ||||||
|     WARN_MSG("Connection downloaded bytes should be a counter, but has decreased in value"); |     MEDIUM_MSG("Connection downloaded bytes should be a counter, but has decreased in value"); | ||||||
|     connDown[idx] = connections.getDown(idx); |     connDown[idx] = connections.getDown(idx); | ||||||
|   } |   } | ||||||
|   if (connections.getUp(idx) < connUp[idx]){ |   if (connections.getUp(idx) < connUp[idx]){ | ||||||
|     WARN_MSG("Connection uploaded bytes should be a counter, but has decreased in value"); |     MEDIUM_MSG("Connection uploaded bytes should be a counter, but has decreased in value"); | ||||||
|     connUp[idx] = connections.getUp(idx); |     connUp[idx] = connections.getUp(idx); | ||||||
|   } |   } | ||||||
|   if (connections.getPacketCount(idx) < connPktcount[idx]){ |   if (connections.getPacketCount(idx) < connPktcount[idx]){ | ||||||
|     WARN_MSG("Connection packet count should be a counter, but has decreased in value"); |     MEDIUM_MSG("Connection packet count should be a counter, but has decreased in value"); | ||||||
|     connPktcount[idx] = connections.getPacketCount(idx); |     connPktcount[idx] = connections.getPacketCount(idx); | ||||||
|   } |   } | ||||||
|   if (connections.getPacketLostCount(idx) < connPktloss[idx]){ |   if (connections.getPacketLostCount(idx) < connPktloss[idx]){ | ||||||
|     WARN_MSG("Connection packet loss count should be a counter, but has decreased in value"); |     MEDIUM_MSG("Connection packet loss count should be a counter, but has decreased in value"); | ||||||
|     connPktloss[idx] = connections.getPacketLostCount(idx); |     connPktloss[idx] = connections.getPacketLostCount(idx); | ||||||
|   } |   } | ||||||
|   if (connections.getPacketRetransmitCount(idx) < connPktretrans[idx]){ |   if (connections.getPacketRetransmitCount(idx) < connPktretrans[idx]){ | ||||||
|     WARN_MSG("Connection packets retransmitted should be a counter, but has decreased in value"); |     MEDIUM_MSG("Connection packets retransmitted should be a counter, but has decreased in value"); | ||||||
|     connPktretrans[idx] = connections.getPacketRetransmitCount(idx); |     connPktretrans[idx] = connections.getPacketRetransmitCount(idx); | ||||||
|   } |   } | ||||||
|   // Add increase in stats to global stats
 |   // Add increase in stats to global stats
 | ||||||
|  | @ -218,7 +218,7 @@ int main(int argc, char **argv){ | ||||||
|       return 0; |       return 0; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|    | 
 | ||||||
|   // Claim a spot in shared memory for this session on the global statistics page
 |   // Claim a spot in shared memory for this session on the global statistics page
 | ||||||
|   sessions.reload(); |   sessions.reload(); | ||||||
|   if (!sessions){ |   if (!sessions){ | ||||||
|  |  | ||||||
							
								
								
									
										5
									
								
								subprojects/usrsctp.wrap
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								subprojects/usrsctp.wrap
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | [wrap-git] | ||||||
|  | url = https://github.com/sctplab/usrsctp.git | ||||||
|  | revision = 0.9.5.0 | ||||||
|  | depth = 1 | ||||||
|  | 
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma