| 1 | /* |
|---|
| 2 | mediastreamer2 library - modular sound and video processing and streaming |
|---|
| 3 | Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org) |
|---|
| 4 | |
|---|
| 5 | This program is free software; you can redistribute it and/or |
|---|
| 6 | modify it under the terms of the GNU General Public License |
|---|
| 7 | as published by the Free Software Foundation; either version 2 |
|---|
| 8 | of the License, or (at your option) any later version. |
|---|
| 9 | |
|---|
| 10 | This program is distributed in the hope that it will be useful, |
|---|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with this program; if not, write to the Free Software |
|---|
| 17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 18 | */ |
|---|
| 19 | |
|---|
| 20 | #if !defined(WIN32) && !defined(_WIN32_WCE) |
|---|
| 21 | #ifdef __APPLE__ |
|---|
| 22 | #include <sys/types.h> |
|---|
| 23 | #endif |
|---|
| 24 | #include <sys/socket.h> |
|---|
| 25 | #include <netdb.h> |
|---|
| 26 | #endif |
|---|
| 27 | |
|---|
| 28 | #include "mediastreamer2/ice.h" |
|---|
| 29 | #include "mediastreamer2/mscommon.h" |
|---|
| 30 | |
|---|
| 31 | #include <math.h> |
|---|
| 32 | |
|---|
| 33 | static void |
|---|
| 34 | ice_sendtest( struct CandidatePair *remote_candidate, Socket myFd, StunAddress4 *dest, |
|---|
| 35 | const StunAtrString *username, const StunAtrString *password, |
|---|
| 36 | int testNum, bool_t verbose , UInt96 *tid); |
|---|
| 37 | |
|---|
| 38 | static void |
|---|
| 39 | ice_sendtest( struct CandidatePair *remote_candidate, Socket myFd, StunAddress4 *dest, |
|---|
| 40 | const StunAtrString *username, const StunAtrString *password, |
|---|
| 41 | int testNum, bool_t verbose , UInt96 *tid) |
|---|
| 42 | { |
|---|
| 43 | bool_t changePort=FALSE; |
|---|
| 44 | bool_t changeIP=FALSE; |
|---|
| 45 | bool_t discard=FALSE; |
|---|
| 46 | |
|---|
| 47 | StunMessage req; |
|---|
| 48 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 49 | int len = STUN_MAX_MESSAGE_SIZE; |
|---|
| 50 | |
|---|
| 51 | switch (testNum) |
|---|
| 52 | { |
|---|
| 53 | case 1: |
|---|
| 54 | case 10: |
|---|
| 55 | case 11: |
|---|
| 56 | break; |
|---|
| 57 | case 2: |
|---|
| 58 | /* changePort=TRUE; */ |
|---|
| 59 | changeIP=TRUE; |
|---|
| 60 | break; |
|---|
| 61 | case 3: |
|---|
| 62 | changePort=TRUE; |
|---|
| 63 | break; |
|---|
| 64 | case 4: |
|---|
| 65 | changeIP=TRUE; |
|---|
| 66 | break; |
|---|
| 67 | case 5: |
|---|
| 68 | discard=TRUE; |
|---|
| 69 | break; |
|---|
| 70 | default: |
|---|
| 71 | printf("Test %i is unkown\n", testNum); |
|---|
| 72 | return ; /* error */ |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | memset(&req, 0, sizeof(StunMessage)); |
|---|
| 76 | |
|---|
| 77 | stunBuildReqSimple( &req, username, |
|---|
| 78 | changePort , changeIP , |
|---|
| 79 | testNum ); |
|---|
| 80 | req.hasMessageIntegrity=TRUE; |
|---|
| 81 | |
|---|
| 82 | req.hasPriority = TRUE; |
|---|
| 83 | req.priority.priority = (UInt32)(pow((double)2,(double)24)*(110) + pow((double)2,(double)8)*(65535) + pow((double)2,(double)0)*(256 - remote_candidate->remote_candidate.component_id)); |
|---|
| 84 | |
|---|
| 85 | /* TODO: put this parameter only for the candidate selected */ |
|---|
| 86 | if (remote_candidate->connectivity_check==VALID) |
|---|
| 87 | req.hasUseCandidate = TRUE; |
|---|
| 88 | |
|---|
| 89 | if (remote_candidate->rem_controlling==1) |
|---|
| 90 | { |
|---|
| 91 | req.hasIceControlled = TRUE; |
|---|
| 92 | req.iceControlled.value = remote_candidate->tiebreak_value; |
|---|
| 93 | } |
|---|
| 94 | else |
|---|
| 95 | { |
|---|
| 96 | req.hasIceControlling = TRUE; |
|---|
| 97 | req.iceControlling.value = remote_candidate->tiebreak_value; |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | /* TODO: not yet implemented? */ |
|---|
| 101 | req.hasFingerprint = TRUE; |
|---|
| 102 | |
|---|
| 103 | len = stunEncodeMessage( &req, buf, len, password ); |
|---|
| 104 | |
|---|
| 105 | memcpy(tid , &(req.msgHdr.tr_id), sizeof(req.msgHdr.tr_id)); |
|---|
| 106 | |
|---|
| 107 | sendMessage( myFd, buf, len, dest->addr, dest->port ); |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | int ice_sound_send_stun_request(RtpSession *session, struct CandidatePair *remote_candidates, int round) |
|---|
| 111 | { |
|---|
| 112 | int roll=250; |
|---|
| 113 | #if 0 |
|---|
| 114 | /* in "passive" mode (UA not behind a NATor behind a full cone NAT), |
|---|
| 115 | wait a few delay before sending the first STUN request: |
|---|
| 116 | this help to traverse */ |
|---|
| 117 | if (session->setup_passive>0) |
|---|
| 118 | { |
|---|
| 119 | return 0; |
|---|
| 120 | } |
|---|
| 121 | #endif |
|---|
| 122 | |
|---|
| 123 | if (remote_candidates==NULL) |
|---|
| 124 | return 0; |
|---|
| 125 | |
|---|
| 126 | if (round>500) |
|---|
| 127 | roll=2*roll; |
|---|
| 128 | |
|---|
| 129 | if (round%roll==50 || round==10) |
|---|
| 130 | { |
|---|
| 131 | int pos; |
|---|
| 132 | |
|---|
| 133 | /* prepare ONCE tie-break value */ |
|---|
| 134 | if (remote_candidates->tiebreak_value==0) { |
|---|
| 135 | remote_candidates->tiebreak_value = random() * (0x7fffffffffffffff/0x7fff); |
|---|
| 136 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 137 | { |
|---|
| 138 | remote_candidates[pos].tiebreak_value = remote_candidates[0].tiebreak_value; |
|---|
| 139 | remote_candidates[pos].rem_controlling = remote_candidates[0].rem_controlling; |
|---|
| 140 | } |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 144 | { |
|---|
| 145 | int media_socket = rtp_session_get_rtp_socket(session); |
|---|
| 146 | StunAddress4 stunServerAddr; |
|---|
| 147 | StunAtrString username; |
|---|
| 148 | StunAtrString password; |
|---|
| 149 | bool_t res; |
|---|
| 150 | #if 0 |
|---|
| 151 | int pad_size; |
|---|
| 152 | #endif |
|---|
| 153 | |
|---|
| 154 | struct CandidatePair *cand_pair = &remote_candidates[pos]; |
|---|
| 155 | username.sizeValue = 0; |
|---|
| 156 | password.sizeValue = 0; |
|---|
| 157 | |
|---|
| 158 | /* username comes from "ice-ufrag" (rfrag:lfrag) */ |
|---|
| 159 | /* ufrag and pwd are in first row only */ |
|---|
| 160 | snprintf(username.value, sizeof(username.value), "%s:%s", |
|---|
| 161 | remote_candidates[0].rem_ice_ufrag, |
|---|
| 162 | remote_candidates[0].loc_ice_ufrag); |
|---|
| 163 | username.sizeValue = (UInt16)strlen(username.value); |
|---|
| 164 | |
|---|
| 165 | |
|---|
| 166 | snprintf(password.value, sizeof(password.value), "%s", |
|---|
| 167 | remote_candidates[0].rem_ice_pwd); |
|---|
| 168 | password.sizeValue = (UInt16)strlen(password.value); |
|---|
| 169 | |
|---|
| 170 | |
|---|
| 171 | res = stunParseServerName(cand_pair->remote_candidate.conn_addr, |
|---|
| 172 | &stunServerAddr); |
|---|
| 173 | if ( res == TRUE ) |
|---|
| 174 | { |
|---|
| 175 | stunServerAddr.port = cand_pair->remote_candidate.conn_port; |
|---|
| 176 | ice_sendtest(&remote_candidates[pos], media_socket, &stunServerAddr, &username, &password, 1, 0/*FALSE*/, |
|---|
| 177 | &(cand_pair->tid)); |
|---|
| 178 | } |
|---|
| 179 | } |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | return 0; |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | static int |
|---|
| 186 | _ice_get_localip_for (struct sockaddr_storage *saddr, size_t saddr_len, char *loc, int size) |
|---|
| 187 | { |
|---|
| 188 | int err, tmp; |
|---|
| 189 | int sock; |
|---|
| 190 | struct sockaddr_storage addr; |
|---|
| 191 | socklen_t addr_len; |
|---|
| 192 | |
|---|
| 193 | strcpy (loc, "127.0.0.1"); /* always fallback to local loopback */ |
|---|
| 194 | |
|---|
| 195 | sock = socket (saddr->ss_family, SOCK_DGRAM, 0); |
|---|
| 196 | tmp = 1; |
|---|
| 197 | err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &tmp, sizeof (int)); |
|---|
| 198 | if (err < 0) |
|---|
| 199 | { |
|---|
| 200 | ms_error("ice.c: Error in setsockopt"); |
|---|
| 201 | closesocket (sock); |
|---|
| 202 | return -1; |
|---|
| 203 | } |
|---|
| 204 | err = connect (sock, (struct sockaddr*)saddr, saddr_len); |
|---|
| 205 | if (err < 0) |
|---|
| 206 | { |
|---|
| 207 | ms_error("ice.c: Error in connect"); |
|---|
| 208 | closesocket (sock); |
|---|
| 209 | return -1; |
|---|
| 210 | } |
|---|
| 211 | addr_len = sizeof (addr); |
|---|
| 212 | err = getsockname (sock, (struct sockaddr *) &addr, (socklen_t*)&addr_len); |
|---|
| 213 | if (err != 0) |
|---|
| 214 | { |
|---|
| 215 | ms_error("ice.c: Error in getsockname"); |
|---|
| 216 | closesocket (sock); |
|---|
| 217 | return -1; |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | err = getnameinfo ((struct sockaddr *) &addr, addr_len, loc, size, NULL, 0, NI_NUMERICHOST); |
|---|
| 221 | if (err != 0) |
|---|
| 222 | { |
|---|
| 223 | ms_error("ice.c: Error in getnameinfo"); |
|---|
| 224 | closesocket (sock); |
|---|
| 225 | return -1; |
|---|
| 226 | } |
|---|
| 227 | closesocket (sock); |
|---|
| 228 | /* ms_message("ice.c: Outgoing interface for sending STUN answer is %s", loc); */ |
|---|
| 229 | return 0; |
|---|
| 230 | } |
|---|
| 231 | |
|---|
| 232 | static void |
|---|
| 233 | _ice_createErrorResponse(StunMessage *response, int cl, int number, const char* msg) |
|---|
| 234 | { |
|---|
| 235 | response->msgHdr.msgType = (STUN_METHOD_BINDING | STUN_ERR_RESP); |
|---|
| 236 | response->hasErrorCode = TRUE; |
|---|
| 237 | response->errorCode.errorClass = cl; |
|---|
| 238 | response->errorCode.number = number; |
|---|
| 239 | strcpy(response->errorCode.reason, msg); |
|---|
| 240 | response->errorCode.sizeReason = strlen(msg); |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | int ice_process_stun_message(RtpSession *session, struct CandidatePair *remote_candidates, OrtpEvent *evt) |
|---|
| 244 | { |
|---|
| 245 | int switch_to_address = -1; |
|---|
| 246 | StunMessage msg; |
|---|
| 247 | bool_t res; |
|---|
| 248 | int already_worked_once=-1; |
|---|
| 249 | OrtpEventData *evt_data = ortp_event_get_data(evt); |
|---|
| 250 | mblk_t *mp = evt_data->packet; |
|---|
| 251 | struct sockaddr_in *udp_remote; |
|---|
| 252 | char src6host[NI_MAXHOST]; |
|---|
| 253 | int recvport = 0; |
|---|
| 254 | int i; |
|---|
| 255 | |
|---|
| 256 | udp_remote = (struct sockaddr_in*)&evt_data->ep->addr; |
|---|
| 257 | |
|---|
| 258 | memset( &msg, 0 , sizeof(msg) ); |
|---|
| 259 | res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, &msg); |
|---|
| 260 | if (!res) |
|---|
| 261 | { |
|---|
| 262 | ms_error("ice.c: Malformed STUN packet."); |
|---|
| 263 | return -1; |
|---|
| 264 | } |
|---|
| 265 | |
|---|
| 266 | if (remote_candidates==NULL) |
|---|
| 267 | { |
|---|
| 268 | ms_error("ice.c: dropping STUN packet: ice is not configured"); |
|---|
| 269 | return -1; |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | /* prepare ONCE tie-break value */ |
|---|
| 273 | if (remote_candidates->tiebreak_value==0) { |
|---|
| 274 | int pos; |
|---|
| 275 | remote_candidates->tiebreak_value = random() * (0x7fffffffffffffff/0x7fff); |
|---|
| 276 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 277 | { |
|---|
| 278 | remote_candidates[pos].tiebreak_value = remote_candidates[0].tiebreak_value; |
|---|
| 279 | remote_candidates[pos].rem_controlling = remote_candidates[0].rem_controlling; |
|---|
| 280 | } |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | memset (src6host, 0, sizeof (src6host)); |
|---|
| 284 | |
|---|
| 285 | { |
|---|
| 286 | struct sockaddr_storage *aaddr = (struct sockaddr_storage *)&evt_data->ep->addr; |
|---|
| 287 | if (aaddr->ss_family==AF_INET) |
|---|
| 288 | recvport = ntohs (((struct sockaddr_in *) udp_remote)->sin_port); |
|---|
| 289 | else |
|---|
| 290 | recvport = ntohs (((struct sockaddr_in6 *) &evt_data->ep->addr)->sin6_port); |
|---|
| 291 | } |
|---|
| 292 | i = getnameinfo ((struct sockaddr*)&evt_data->ep->addr, evt_data->ep->addrlen, |
|---|
| 293 | src6host, NI_MAXHOST, |
|---|
| 294 | NULL, 0, NI_NUMERICHOST); |
|---|
| 295 | if (i != 0) |
|---|
| 296 | { |
|---|
| 297 | ms_error("ice.c: Error with getnameinfo"); |
|---|
| 298 | } else |
|---|
| 299 | { |
|---|
| 300 | if (STUN_IS_REQUEST(msg.msgHdr.msgType)) |
|---|
| 301 | ms_message("ice.c: Request received from: %s:%i", |
|---|
| 302 | src6host, recvport); |
|---|
| 303 | else |
|---|
| 304 | ms_message("ice.c: Answer received from: %s:%i", |
|---|
| 305 | src6host, recvport); |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | { |
|---|
| 309 | int pos; |
|---|
| 310 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 311 | { |
|---|
| 312 | struct CandidatePair *cand_pair = &remote_candidates[pos]; |
|---|
| 313 | #ifdef RESTRICTIVE_ICE |
|---|
| 314 | if (cand_pair->connectivity_check == VALID |
|---|
| 315 | ||cand_pair->connectivity_check == RECV_VALID) |
|---|
| 316 | { |
|---|
| 317 | already_worked_once=pos; |
|---|
| 318 | break; |
|---|
| 319 | } |
|---|
| 320 | #else |
|---|
| 321 | if (cand_pair->connectivity_check == VALID |
|---|
| 322 | ||cand_pair->connectivity_check == RECV_VALID |
|---|
| 323 | ||cand_pair->connectivity_check == SEND_VALID) |
|---|
| 324 | { |
|---|
| 325 | already_worked_once=pos; |
|---|
| 326 | break; |
|---|
| 327 | } |
|---|
| 328 | #endif |
|---|
| 329 | } |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | if (STUN_IS_REQUEST(msg.msgHdr.msgType)) |
|---|
| 333 | { |
|---|
| 334 | StunMessage resp; |
|---|
| 335 | StunAtrString hmacPassword; |
|---|
| 336 | StunAddress4 remote_addr; |
|---|
| 337 | int rtp_socket; |
|---|
| 338 | |
|---|
| 339 | memset( &resp, 0 , sizeof(resp)); |
|---|
| 340 | remote_addr.addr = ntohl(udp_remote->sin_addr.s_addr); |
|---|
| 341 | remote_addr.port = ntohs(udp_remote->sin_port); |
|---|
| 342 | |
|---|
| 343 | rtp_socket = rtp_session_get_rtp_socket(session); |
|---|
| 344 | |
|---|
| 345 | resp.msgHdr.magic_cookie = ntohl(msg.msgHdr.magic_cookie); |
|---|
| 346 | for (i=0; i<12; i++ ) |
|---|
| 347 | { |
|---|
| 348 | resp.msgHdr.tr_id.octet[i] = msg.msgHdr.tr_id.octet[i]; |
|---|
| 349 | } |
|---|
| 350 | |
|---|
| 351 | /* check mandatory params */ |
|---|
| 352 | |
|---|
| 353 | if (!msg.hasUsername) |
|---|
| 354 | { |
|---|
| 355 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 356 | int len = sizeof(buf); |
|---|
| 357 | ms_error("Missing USERNAME attribute in connectivity check"); |
|---|
| 358 | _ice_createErrorResponse(&resp, 4, 32, "Missing USERNAME attribute"); |
|---|
| 359 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 360 | if (len) |
|---|
| 361 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 362 | return -1; |
|---|
| 363 | } |
|---|
| 364 | if (!msg.hasMessageIntegrity) |
|---|
| 365 | { |
|---|
| 366 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 367 | int len = sizeof(buf); |
|---|
| 368 | ms_error("Missing MESSAGEINTEGRITY attribute in connectivity check"); |
|---|
| 369 | _ice_createErrorResponse(&resp, 4, 1, "Missing MESSAGEINTEGRITY attribute"); |
|---|
| 370 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 371 | if (len) |
|---|
| 372 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 373 | return -1; |
|---|
| 374 | } |
|---|
| 375 | |
|---|
| 376 | if (remote_candidates==NULL) |
|---|
| 377 | { |
|---|
| 378 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 379 | int len = sizeof(buf); |
|---|
| 380 | ms_error("no password for checking MESSAGEINTEGRITY"); |
|---|
| 381 | _ice_createErrorResponse(&resp, 4, 1, "no password for checking MESSAGEINTEGRITY"); |
|---|
| 382 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 383 | if (len) |
|---|
| 384 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 385 | return -1; |
|---|
| 386 | } |
|---|
| 387 | /* |
|---|
| 388 | The password associated with that transport address ID is used to verify |
|---|
| 389 | the MESSAGE-INTEGRITY attribute, if one was present in the request. |
|---|
| 390 | */ |
|---|
| 391 | char hmac[20]; |
|---|
| 392 | stunCalculateIntegrity_shortterm(hmac, (char*)mp->b_rptr, mp->b_wptr-mp->b_rptr-24, remote_candidates[0].loc_ice_pwd); |
|---|
| 393 | if (memcmp(msg.messageIntegrity.hash, hmac, 20)!=0) |
|---|
| 394 | { |
|---|
| 395 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 396 | int len = sizeof(buf); |
|---|
| 397 | ms_error("Wrong MESSAGEINTEGRITY attribute in connectivity check"); |
|---|
| 398 | _ice_createErrorResponse(&resp, 4, 1, "Wrong MESSAGEINTEGRITY attribute"); |
|---|
| 399 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 400 | if (len) |
|---|
| 401 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 402 | return -1; |
|---|
| 403 | } |
|---|
| 404 | |
|---|
| 405 | |
|---|
| 406 | /* 7.2.1.1. Detecting and Repairing Role Conflicts */ |
|---|
| 407 | /* TODO */ |
|---|
| 408 | if (!msg.hasIceControlling && !msg.hasIceControlled) |
|---|
| 409 | { |
|---|
| 410 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 411 | int len = sizeof(buf); |
|---|
| 412 | ms_error("Missing either ICE-CONTROLLING or ICE-CONTROLLED attribute"); |
|---|
| 413 | _ice_createErrorResponse(&resp, 4, 87, "Missing either ICE-CONTROLLING or ICE-CONTROLLED attribute"); |
|---|
| 414 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 415 | if (len) |
|---|
| 416 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 417 | return -1; |
|---|
| 418 | } |
|---|
| 419 | |
|---|
| 420 | if (remote_candidates[0].rem_controlling==0 && msg.hasIceControlling) { |
|---|
| 421 | /* If the agent's tie-breaker is larger than or equal |
|---|
| 422 | to the contents of the ICE-CONTROLLING attribute |
|---|
| 423 | -> send 487, and do not change ROLE */ |
|---|
| 424 | if (remote_candidates[0].tiebreak_value >= msg.iceControlling.value) { |
|---|
| 425 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 426 | int len = sizeof(buf); |
|---|
| 427 | ms_error("487 Role Conflict"); |
|---|
| 428 | _ice_createErrorResponse(&resp, 4, 87, "Role Conflict"); |
|---|
| 429 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 430 | if (len) |
|---|
| 431 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 432 | return -1; |
|---|
| 433 | } |
|---|
| 434 | else { |
|---|
| 435 | int pos; |
|---|
| 436 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 437 | { |
|---|
| 438 | remote_candidates[pos].rem_controlling = 1; |
|---|
| 439 | } |
|---|
| 440 | /* TODO: compute again priority */ |
|---|
| 441 | } |
|---|
| 442 | } |
|---|
| 443 | |
|---|
| 444 | if (remote_candidates[0].rem_controlling==1 && msg.hasIceControlled) { |
|---|
| 445 | |
|---|
| 446 | /* If the agent's tie-breaker is larger than or equal |
|---|
| 447 | to the contents of the ICE-CONTROLLED attribute |
|---|
| 448 | -> change ROLE */ |
|---|
| 449 | if (remote_candidates[0].tiebreak_value >= msg.iceControlled.value) { |
|---|
| 450 | int pos; |
|---|
| 451 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 452 | { |
|---|
| 453 | remote_candidates[pos].rem_controlling = 0; |
|---|
| 454 | } |
|---|
| 455 | /* TODO: compute again priority */ |
|---|
| 456 | } |
|---|
| 457 | else { |
|---|
| 458 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 459 | int len = sizeof(buf); |
|---|
| 460 | ms_error("487 Role Conflict"); |
|---|
| 461 | _ice_createErrorResponse(&resp, 4, 87, "Role Conflict"); |
|---|
| 462 | len = stunEncodeMessage(&resp, buf, len, &hmacPassword ); |
|---|
| 463 | if (len) |
|---|
| 464 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 465 | return -1; |
|---|
| 466 | } |
|---|
| 467 | } |
|---|
| 468 | |
|---|
| 469 | /* 7.2.1.3. Learning Peer Reflexive Candidates */ |
|---|
| 470 | #if 0 |
|---|
| 471 | if () /* found in table */ |
|---|
| 472 | { |
|---|
| 473 | resp.hasPriority = TRUE; |
|---|
| 474 | resp.priority.priority = msg.priority.priority; |
|---|
| 475 | } |
|---|
| 476 | #endif |
|---|
| 477 | |
|---|
| 478 | /* TODO: the current algo is not ICE, but give similar results */ |
|---|
| 479 | int pos; |
|---|
| 480 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 481 | { |
|---|
| 482 | struct CandidatePair *cand_pair = &remote_candidates[pos]; |
|---|
| 483 | |
|---|
| 484 | if (cand_pair->connectivity_check == VALID) |
|---|
| 485 | { |
|---|
| 486 | /* already found a valid check with highest priority */ |
|---|
| 487 | break; |
|---|
| 488 | } |
|---|
| 489 | |
|---|
| 490 | /* connectivity check is coming from a known remote candidate? |
|---|
| 491 | we should also check the port... |
|---|
| 492 | */ |
|---|
| 493 | if (strcmp(cand_pair->remote_candidate.conn_addr, src6host)==0) |
|---|
| 494 | { |
|---|
| 495 | /* working one way: use it */ |
|---|
| 496 | switch_to_address = pos; |
|---|
| 497 | if (cand_pair->connectivity_check == RECV_VALID |
|---|
| 498 | || cand_pair->connectivity_check == VALID) |
|---|
| 499 | { |
|---|
| 500 | if (cand_pair->connectivity_check != VALID) |
|---|
| 501 | { |
|---|
| 502 | switch_to_address = pos; |
|---|
| 503 | ms_message("ice.c: candidate id (index=%i) moved in VALID state (stunbindingrequest received).", pos); |
|---|
| 504 | cand_pair->connectivity_check = VALID; |
|---|
| 505 | } |
|---|
| 506 | } |
|---|
| 507 | else |
|---|
| 508 | cand_pair->connectivity_check = SEND_VALID; |
|---|
| 509 | |
|---|
| 510 | /* we have a VALID one */ |
|---|
| 511 | } |
|---|
| 512 | } |
|---|
| 513 | |
|---|
| 514 | |
|---|
| 515 | UInt32 cookie = 0x2112A442; |
|---|
| 516 | resp.hasXorMappedAddress = TRUE; |
|---|
| 517 | resp.xorMappedAddress.ipv4.port = remote_addr.port^(cookie>>16); |
|---|
| 518 | resp.xorMappedAddress.ipv4.addr = remote_addr.addr^cookie; |
|---|
| 519 | |
|---|
| 520 | |
|---|
| 521 | resp.msgHdr.msgType = (STUN_METHOD_BINDING | STUN_SUCCESS_RESP); |
|---|
| 522 | |
|---|
| 523 | resp.hasUsername = TRUE; |
|---|
| 524 | memcpy(resp.username.value, msg.username.value, msg.username.sizeValue ); |
|---|
| 525 | resp.username.sizeValue = msg.username.sizeValue; |
|---|
| 526 | |
|---|
| 527 | /* ? any messageintegrity in response? */ |
|---|
| 528 | resp.hasMessageIntegrity = TRUE; |
|---|
| 529 | |
|---|
| 530 | const char serverName[] = "mediastreamer2 " STUN_VERSION; |
|---|
| 531 | resp.hasSoftware = TRUE; |
|---|
| 532 | memcpy( resp.softwareName.value, serverName, sizeof(serverName)); |
|---|
| 533 | resp.softwareName.sizeValue = sizeof(serverName); |
|---|
| 534 | |
|---|
| 535 | { |
|---|
| 536 | char buf[STUN_MAX_MESSAGE_SIZE]; |
|---|
| 537 | int len = sizeof(buf); |
|---|
| 538 | len = stunEncodeMessage( &resp, buf, len, &hmacPassword ); |
|---|
| 539 | if (len) |
|---|
| 540 | sendMessage( rtp_socket, buf, len, remote_addr.addr, remote_addr.port); |
|---|
| 541 | } |
|---|
| 542 | } |
|---|
| 543 | else if (STUN_IS_SUCCESS_RESP(msg.msgHdr.msgType)) |
|---|
| 544 | { |
|---|
| 545 | /* set state to RECV-VALID or VALID */ |
|---|
| 546 | StunMessage resp; |
|---|
| 547 | StunAddress4 mappedAddr; |
|---|
| 548 | memset(&resp, 0, sizeof(StunMessage)); |
|---|
| 549 | res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, |
|---|
| 550 | &resp ); |
|---|
| 551 | if (!res) |
|---|
| 552 | { |
|---|
| 553 | ms_error("ice.c: Bad format for STUN answer."); |
|---|
| 554 | return -1; |
|---|
| 555 | } |
|---|
| 556 | |
|---|
| 557 | mappedAddr = resp.mappedAddress.ipv4; |
|---|
| 558 | |
|---|
| 559 | if (remote_candidates!=NULL) { |
|---|
| 560 | int pos; |
|---|
| 561 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 562 | { |
|---|
| 563 | struct CandidatePair *cand_pair = &remote_candidates[pos]; |
|---|
| 564 | |
|---|
| 565 | if (memcmp(&(cand_pair->tid), &(resp.msgHdr.tr_id), sizeof(resp.msgHdr.tr_id))==0) |
|---|
| 566 | { |
|---|
| 567 | /* Youhouhouhou */ |
|---|
| 568 | if (cand_pair->connectivity_check != VALID) |
|---|
| 569 | { |
|---|
| 570 | switch_to_address = pos; |
|---|
| 571 | } |
|---|
| 572 | #if 0 |
|---|
| 573 | ms_message("ice.c: Valid STUN answer received (to=%s:%i from=%s:%i)", |
|---|
| 574 | cand_pair->remote_candidate.ipaddr, cand_pair->remote_candidate.conn_port, |
|---|
| 575 | src6host, recvport); |
|---|
| 576 | #endif |
|---|
| 577 | if (cand_pair->connectivity_check == SEND_VALID |
|---|
| 578 | || cand_pair->connectivity_check == VALID) |
|---|
| 579 | { |
|---|
| 580 | if (cand_pair->connectivity_check != VALID) |
|---|
| 581 | { |
|---|
| 582 | ms_message("ice.c: Switch to VALID mode for (to=%s:%i from=%s:%i)", |
|---|
| 583 | cand_pair->remote_candidate.conn_addr, cand_pair->remote_candidate.conn_port, |
|---|
| 584 | src6host, recvport); |
|---|
| 585 | cand_pair->connectivity_check = VALID; |
|---|
| 586 | } |
|---|
| 587 | } |
|---|
| 588 | else |
|---|
| 589 | cand_pair->connectivity_check = RECV_VALID; |
|---|
| 590 | } |
|---|
| 591 | } |
|---|
| 592 | } |
|---|
| 593 | } |
|---|
| 594 | else if (STUN_IS_ERR_RESP(msg.msgHdr.msgType)) |
|---|
| 595 | { |
|---|
| 596 | StunMessage resp; |
|---|
| 597 | memset(&resp, 0, sizeof(StunMessage)); |
|---|
| 598 | res = stunParseMessage((char*)mp->b_rptr, mp->b_wptr-mp->b_rptr, |
|---|
| 599 | &resp ); |
|---|
| 600 | if (!res) |
|---|
| 601 | { |
|---|
| 602 | ms_error("ice.c: Bad format for STUN answer."); |
|---|
| 603 | return -1; |
|---|
| 604 | } |
|---|
| 605 | |
|---|
| 606 | if (remote_candidates!=NULL) { |
|---|
| 607 | int pos; |
|---|
| 608 | for (pos=0;pos<10 && remote_candidates[pos].remote_candidate.conn_addr[0]!='\0';pos++) |
|---|
| 609 | { |
|---|
| 610 | struct CandidatePair *cand_pair = &remote_candidates[pos]; |
|---|
| 611 | |
|---|
| 612 | if (memcmp(&(cand_pair->tid), &(resp.msgHdr.tr_id), sizeof(resp.msgHdr.tr_id))==0) |
|---|
| 613 | { |
|---|
| 614 | if (resp.hasErrorCode==TRUE && resp.errorCode.errorClass==4 && resp.errorCode.number==87) |
|---|
| 615 | { |
|---|
| 616 | /* change role */ |
|---|
| 617 | if (remote_candidates[0].rem_controlling==1) |
|---|
| 618 | remote_candidates[0].rem_controlling=0; |
|---|
| 619 | else |
|---|
| 620 | remote_candidates[0].rem_controlling=1; |
|---|
| 621 | /* TODO: compute again priority */ |
|---|
| 622 | } |
|---|
| 623 | } |
|---|
| 624 | } |
|---|
| 625 | } |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | if (remote_candidates==NULL) { |
|---|
| 629 | ms_warning("ice.c: STUN connectivity check is disabled but we received a STUN message (%s:%i)\n", |
|---|
| 630 | src6host, recvport); |
|---|
| 631 | return 0; |
|---|
| 632 | } |
|---|
| 633 | if (switch_to_address == -1) |
|---|
| 634 | return 0; |
|---|
| 635 | |
|---|
| 636 | { |
|---|
| 637 | /* skip symmetric RTP if any previous connection is working */ |
|---|
| 638 | if (switch_to_address<already_worked_once || already_worked_once==-1) |
|---|
| 639 | { |
|---|
| 640 | /* rtp_in_direct_mode = 1; */ |
|---|
| 641 | /* current destination address: snprintf(rtp_remote_addr, 256, "%s:%i", src6host, recvport); */ |
|---|
| 642 | ms_warning("ice.c: Modifying remote destination for RTP stream (%s:%i)\n", |
|---|
| 643 | src6host, recvport); |
|---|
| 644 | memcpy(&session->rtp.rem_addr, &evt_data->ep->addr, evt_data->ep->addrlen); |
|---|
| 645 | session->rtp.rem_addrlen=evt_data->ep->addrlen; |
|---|
| 646 | } |
|---|
| 647 | } |
|---|
| 648 | return 0; |
|---|
| 649 | } |
|---|
| 650 | |
|---|