James Kuszmaul | 82f6c04 | 2021-01-17 11:30:16 -0800 | [diff] [blame^] | 1 | /** |
| 2 | * @file stun/attr.c STUN Attributes |
| 3 | * |
| 4 | * Copyright (C) 2010 Creytiv.com |
| 5 | */ |
| 6 | #include <re_types.h> |
| 7 | #include <re_mem.h> |
| 8 | #include <re_mbuf.h> |
| 9 | #include <re_sa.h> |
| 10 | #include <re_list.h> |
| 11 | #include <re_fmt.h> |
| 12 | #include <re_sys.h> |
| 13 | #include <re_stun.h> |
| 14 | #include "stun.h" |
| 15 | |
| 16 | |
| 17 | static int str_decode(struct mbuf *mb, char **str, size_t len) |
| 18 | { |
| 19 | if (mbuf_get_left(mb) < len) |
| 20 | return EBADMSG; |
| 21 | |
| 22 | return mbuf_strdup(mb, str, len); |
| 23 | } |
| 24 | |
| 25 | |
| 26 | static void destructor(void *arg) |
| 27 | { |
| 28 | struct stun_attr *attr = arg; |
| 29 | |
| 30 | switch (attr->type) { |
| 31 | |
| 32 | case STUN_ATTR_USERNAME: |
| 33 | case STUN_ATTR_REALM: |
| 34 | case STUN_ATTR_NONCE: |
| 35 | case STUN_ATTR_SOFTWARE: |
| 36 | mem_deref(attr->v.str); |
| 37 | break; |
| 38 | |
| 39 | case STUN_ATTR_ERR_CODE: |
| 40 | mem_deref(attr->v.err_code.reason); |
| 41 | break; |
| 42 | |
| 43 | case STUN_ATTR_DATA: |
| 44 | case STUN_ATTR_PADDING: |
| 45 | mem_deref(attr->v.mb.buf); |
| 46 | break; |
| 47 | } |
| 48 | |
| 49 | list_unlink(&attr->le); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | int stun_attr_encode(struct mbuf *mb, uint16_t type, const void *v, |
| 54 | const uint8_t *tid, uint8_t padding) |
| 55 | { |
| 56 | const struct stun_change_req *ch_req = v; |
| 57 | const struct stun_errcode *err_code = v; |
| 58 | const struct stun_unknown_attr *ua = v; |
| 59 | const uint32_t *num32 = v; |
| 60 | const uint16_t *num16 = v; |
| 61 | const uint8_t *num8 = v; |
| 62 | const struct mbuf *mbd = v; |
| 63 | size_t start, len; |
| 64 | uint32_t i, n; |
| 65 | int err = 0; |
| 66 | |
| 67 | if (!mb || !v) |
| 68 | return EINVAL; |
| 69 | |
| 70 | mb->pos += 4; |
| 71 | start = mb->pos; |
| 72 | |
| 73 | switch (type) { |
| 74 | |
| 75 | case STUN_ATTR_MAPPED_ADDR: |
| 76 | case STUN_ATTR_ALT_SERVER: |
| 77 | case STUN_ATTR_RESP_ORIGIN: |
| 78 | case STUN_ATTR_OTHER_ADDR: |
| 79 | tid = NULL; |
| 80 | /*@fallthrough@*/ |
| 81 | case STUN_ATTR_XOR_PEER_ADDR: |
| 82 | case STUN_ATTR_XOR_RELAY_ADDR: |
| 83 | case STUN_ATTR_XOR_MAPPED_ADDR: |
| 84 | err |= stun_addr_encode(mb, v, tid); |
| 85 | break; |
| 86 | |
| 87 | case STUN_ATTR_CHANGE_REQ: |
| 88 | n = (uint32_t)ch_req->ip << 2 | (uint32_t)ch_req->port << 1; |
| 89 | err |= mbuf_write_u32(mb, htonl(n)); |
| 90 | break; |
| 91 | |
| 92 | case STUN_ATTR_USERNAME: |
| 93 | case STUN_ATTR_REALM: |
| 94 | case STUN_ATTR_NONCE: |
| 95 | case STUN_ATTR_SOFTWARE: |
| 96 | err |= mbuf_write_str(mb, v); |
| 97 | break; |
| 98 | |
| 99 | case STUN_ATTR_MSG_INTEGRITY: |
| 100 | err |= mbuf_write_mem(mb, v, 20); |
| 101 | break; |
| 102 | |
| 103 | case STUN_ATTR_ERR_CODE: |
| 104 | err |= mbuf_write_u16(mb, 0x00); |
| 105 | err |= mbuf_write_u8(mb, err_code->code/100); |
| 106 | err |= mbuf_write_u8(mb, err_code->code%100); |
| 107 | err |= mbuf_write_str(mb, err_code->reason); |
| 108 | break; |
| 109 | |
| 110 | case STUN_ATTR_UNKNOWN_ATTR: |
| 111 | for (i=0; i<ua->typec; i++) |
| 112 | err |= mbuf_write_u16(mb, htons(ua->typev[i])); |
| 113 | break; |
| 114 | |
| 115 | case STUN_ATTR_CHANNEL_NUMBER: |
| 116 | case STUN_ATTR_RESP_PORT: |
| 117 | err |= mbuf_write_u16(mb, htons(*num16)); |
| 118 | err |= mbuf_write_u16(mb, 0x0000); |
| 119 | break; |
| 120 | |
| 121 | case STUN_ATTR_LIFETIME: |
| 122 | case STUN_ATTR_PRIORITY: |
| 123 | case STUN_ATTR_FINGERPRINT: |
| 124 | err |= mbuf_write_u32(mb, htonl(*num32)); |
| 125 | break; |
| 126 | |
| 127 | case STUN_ATTR_DATA: |
| 128 | case STUN_ATTR_PADDING: |
| 129 | if (mb == mbd) { |
| 130 | mb->pos = mb->end; |
| 131 | break; |
| 132 | } |
| 133 | err |= mbuf_write_mem(mb, mbuf_buf(mbd), mbuf_get_left(mbd)); |
| 134 | break; |
| 135 | |
| 136 | case STUN_ATTR_REQ_ADDR_FAMILY: |
| 137 | case STUN_ATTR_REQ_TRANSPORT: |
| 138 | err |= mbuf_write_u8(mb, *num8); |
| 139 | err |= mbuf_write_u8(mb, 0x00); |
| 140 | err |= mbuf_write_u16(mb, 0x0000); |
| 141 | break; |
| 142 | |
| 143 | case STUN_ATTR_EVEN_PORT: |
| 144 | err |= mbuf_write_u8(mb, ((struct stun_even_port *)v)->r << 7); |
| 145 | break; |
| 146 | |
| 147 | case STUN_ATTR_DONT_FRAGMENT: |
| 148 | case STUN_ATTR_USE_CAND: |
| 149 | /* no value */ |
| 150 | break; |
| 151 | |
| 152 | case STUN_ATTR_RSV_TOKEN: |
| 153 | case STUN_ATTR_CONTROLLED: |
| 154 | case STUN_ATTR_CONTROLLING: |
| 155 | err |= mbuf_write_u64(mb, sys_htonll(*(uint64_t *)v)); |
| 156 | break; |
| 157 | |
| 158 | default: |
| 159 | err = EINVAL; |
| 160 | break; |
| 161 | } |
| 162 | |
| 163 | /* header */ |
| 164 | len = mb->pos - start; |
| 165 | |
| 166 | mb->pos = start - 4; |
| 167 | err |= mbuf_write_u16(mb, htons(type)); |
| 168 | err |= mbuf_write_u16(mb, htons(len)); |
| 169 | mb->pos += len; |
| 170 | |
| 171 | /* padding */ |
| 172 | while ((mb->pos - start) & 0x03) |
| 173 | err |= mbuf_write_u8(mb, padding); |
| 174 | |
| 175 | return err; |
| 176 | } |
| 177 | |
| 178 | |
| 179 | int stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb, |
| 180 | const uint8_t *tid, struct stun_unknown_attr *ua) |
| 181 | { |
| 182 | struct stun_attr *attr; |
| 183 | size_t start, len; |
| 184 | uint32_t i, n; |
| 185 | int err = 0; |
| 186 | |
| 187 | if (!mb || !attrp) |
| 188 | return EINVAL; |
| 189 | |
| 190 | if (mbuf_get_left(mb) < 4) |
| 191 | return EBADMSG; |
| 192 | |
| 193 | attr = mem_zalloc(sizeof(*attr), destructor); |
| 194 | if (!attr) |
| 195 | return ENOMEM; |
| 196 | |
| 197 | attr->type = ntohs(mbuf_read_u16(mb)); |
| 198 | len = ntohs(mbuf_read_u16(mb)); |
| 199 | |
| 200 | if (mbuf_get_left(mb) < len) |
| 201 | goto badmsg; |
| 202 | |
| 203 | start = mb->pos; |
| 204 | |
| 205 | switch (attr->type) { |
| 206 | |
| 207 | case STUN_ATTR_MAPPED_ADDR: |
| 208 | case STUN_ATTR_ALT_SERVER: |
| 209 | case STUN_ATTR_RESP_ORIGIN: |
| 210 | case STUN_ATTR_OTHER_ADDR: |
| 211 | tid = NULL; |
| 212 | /*@fallthrough@*/ |
| 213 | case STUN_ATTR_XOR_PEER_ADDR: |
| 214 | case STUN_ATTR_XOR_RELAY_ADDR: |
| 215 | case STUN_ATTR_XOR_MAPPED_ADDR: |
| 216 | err = stun_addr_decode(mb, &attr->v.sa, tid); |
| 217 | break; |
| 218 | |
| 219 | case STUN_ATTR_CHANGE_REQ: |
| 220 | if (len != 4) |
| 221 | goto badmsg; |
| 222 | |
| 223 | n = ntohl(mbuf_read_u32(mb)); |
| 224 | attr->v.change_req.ip = (n >> 2) & 0x1; |
| 225 | attr->v.change_req.port = (n >> 1) & 0x1; |
| 226 | break; |
| 227 | |
| 228 | case STUN_ATTR_USERNAME: |
| 229 | case STUN_ATTR_REALM: |
| 230 | case STUN_ATTR_NONCE: |
| 231 | case STUN_ATTR_SOFTWARE: |
| 232 | err = str_decode(mb, &attr->v.str, len); |
| 233 | break; |
| 234 | |
| 235 | case STUN_ATTR_MSG_INTEGRITY: |
| 236 | if (len != 20) |
| 237 | goto badmsg; |
| 238 | |
| 239 | err = mbuf_read_mem(mb, attr->v.msg_integrity, 20); |
| 240 | break; |
| 241 | |
| 242 | case STUN_ATTR_ERR_CODE: |
| 243 | if (len < 4) |
| 244 | goto badmsg; |
| 245 | |
| 246 | mb->pos += 2; |
| 247 | attr->v.err_code.code = (mbuf_read_u8(mb) & 0x7) * 100; |
| 248 | attr->v.err_code.code += mbuf_read_u8(mb); |
| 249 | err = str_decode(mb, &attr->v.err_code.reason, len - 4); |
| 250 | break; |
| 251 | |
| 252 | case STUN_ATTR_UNKNOWN_ATTR: |
| 253 | for (i=0; i<len/2; i++) { |
| 254 | uint16_t type = ntohs(mbuf_read_u16(mb)); |
| 255 | |
| 256 | if (i >= ARRAY_SIZE(attr->v.unknown_attr.typev)) |
| 257 | continue; |
| 258 | |
| 259 | attr->v.unknown_attr.typev[i] = type; |
| 260 | attr->v.unknown_attr.typec++; |
| 261 | } |
| 262 | break; |
| 263 | |
| 264 | case STUN_ATTR_CHANNEL_NUMBER: |
| 265 | case STUN_ATTR_RESP_PORT: |
| 266 | if (len < 2) |
| 267 | goto badmsg; |
| 268 | |
| 269 | attr->v.uint16 = ntohs(mbuf_read_u16(mb)); |
| 270 | break; |
| 271 | |
| 272 | case STUN_ATTR_LIFETIME: |
| 273 | case STUN_ATTR_PRIORITY: |
| 274 | case STUN_ATTR_FINGERPRINT: |
| 275 | if (len != 4) |
| 276 | goto badmsg; |
| 277 | |
| 278 | attr->v.uint32 = ntohl(mbuf_read_u32(mb)); |
| 279 | break; |
| 280 | |
| 281 | case STUN_ATTR_DATA: |
| 282 | case STUN_ATTR_PADDING: |
| 283 | attr->v.mb.buf = mem_ref(mb->buf); |
| 284 | attr->v.mb.size = mb->size; |
| 285 | attr->v.mb.pos = mb->pos; |
| 286 | attr->v.mb.end = mb->pos + len; |
| 287 | mb->pos += len; |
| 288 | break; |
| 289 | |
| 290 | case STUN_ATTR_REQ_ADDR_FAMILY: |
| 291 | case STUN_ATTR_REQ_TRANSPORT: |
| 292 | if (len < 1) |
| 293 | goto badmsg; |
| 294 | |
| 295 | attr->v.uint8 = mbuf_read_u8(mb); |
| 296 | break; |
| 297 | |
| 298 | case STUN_ATTR_EVEN_PORT: |
| 299 | if (len < 1) |
| 300 | goto badmsg; |
| 301 | |
| 302 | attr->v.even_port.r = (mbuf_read_u8(mb) >> 7) & 0x1; |
| 303 | break; |
| 304 | |
| 305 | case STUN_ATTR_DONT_FRAGMENT: |
| 306 | case STUN_ATTR_USE_CAND: |
| 307 | if (len > 0) |
| 308 | goto badmsg; |
| 309 | |
| 310 | /* no value */ |
| 311 | break; |
| 312 | |
| 313 | case STUN_ATTR_RSV_TOKEN: |
| 314 | case STUN_ATTR_CONTROLLING: |
| 315 | case STUN_ATTR_CONTROLLED: |
| 316 | if (len != 8) |
| 317 | goto badmsg; |
| 318 | |
| 319 | attr->v.uint64 = sys_ntohll(mbuf_read_u64(mb)); |
| 320 | break; |
| 321 | |
| 322 | default: |
| 323 | mb->pos += len; |
| 324 | |
| 325 | if (attr->type >= 0x8000) |
| 326 | break; |
| 327 | |
| 328 | if (ua && ua->typec < ARRAY_SIZE(ua->typev)) |
| 329 | ua->typev[ua->typec++] = attr->type; |
| 330 | break; |
| 331 | } |
| 332 | |
| 333 | if (err) |
| 334 | goto error; |
| 335 | |
| 336 | /* padding */ |
| 337 | while (((mb->pos - start) & 0x03) && mbuf_get_left(mb)) |
| 338 | ++mb->pos; |
| 339 | |
| 340 | *attrp = attr; |
| 341 | |
| 342 | return 0; |
| 343 | |
| 344 | badmsg: |
| 345 | err = EBADMSG; |
| 346 | error: |
| 347 | mem_deref(attr); |
| 348 | |
| 349 | return err; |
| 350 | } |
| 351 | |
| 352 | |
| 353 | /** |
| 354 | * Get the name of a STUN attribute |
| 355 | * |
| 356 | * @param type STUN attribute type |
| 357 | * |
| 358 | * @return String with attribute name |
| 359 | */ |
| 360 | const char *stun_attr_name(uint16_t type) |
| 361 | { |
| 362 | switch (type) { |
| 363 | |
| 364 | case STUN_ATTR_MAPPED_ADDR: return "MAPPED-ADDRESS"; |
| 365 | case STUN_ATTR_CHANGE_REQ: return "CHANGE-REQUEST"; |
| 366 | case STUN_ATTR_USERNAME: return "USERNAME"; |
| 367 | case STUN_ATTR_MSG_INTEGRITY: return "MESSAGE-INTEGRITY"; |
| 368 | case STUN_ATTR_ERR_CODE: return "ERROR-CODE"; |
| 369 | case STUN_ATTR_UNKNOWN_ATTR: return "UNKNOWN-ATTRIBUTE"; |
| 370 | case STUN_ATTR_CHANNEL_NUMBER: return "CHANNEL-NUMBER"; |
| 371 | case STUN_ATTR_LIFETIME: return "LIFETIME"; |
| 372 | case STUN_ATTR_XOR_PEER_ADDR: return "XOR-PEER-ADDRESS"; |
| 373 | case STUN_ATTR_DATA: return "DATA"; |
| 374 | case STUN_ATTR_REALM: return "REALM"; |
| 375 | case STUN_ATTR_NONCE: return "NONCE"; |
| 376 | case STUN_ATTR_XOR_RELAY_ADDR: return "XOR-RELAYED-ADDRESS"; |
| 377 | case STUN_ATTR_REQ_ADDR_FAMILY: return "REQUESTED-ADDRESS-FAMILY"; |
| 378 | case STUN_ATTR_EVEN_PORT: return "EVEN_PORT"; |
| 379 | case STUN_ATTR_REQ_TRANSPORT: return "REQUESTED-TRANSPORT"; |
| 380 | case STUN_ATTR_DONT_FRAGMENT: return "DONT-FRAGMENT"; |
| 381 | case STUN_ATTR_XOR_MAPPED_ADDR: return "XOR-MAPPED-ADDRESS"; |
| 382 | case STUN_ATTR_RSV_TOKEN: return "RESERVATION-TOKEN"; |
| 383 | case STUN_ATTR_PRIORITY: return "PRIORITY"; |
| 384 | case STUN_ATTR_USE_CAND: return "USE-CANDIDATE"; |
| 385 | case STUN_ATTR_PADDING: return "PADDING"; |
| 386 | case STUN_ATTR_RESP_PORT: return "RESPONSE-PORT"; |
| 387 | case STUN_ATTR_SOFTWARE: return "SOFTWARE"; |
| 388 | case STUN_ATTR_ALT_SERVER: return "ALTERNATE-SERVER"; |
| 389 | case STUN_ATTR_FINGERPRINT: return "FINGERPRINT"; |
| 390 | case STUN_ATTR_CONTROLLING: return "ICE-CONTROLLING"; |
| 391 | case STUN_ATTR_CONTROLLED: return "ICE-CONTROLLED"; |
| 392 | case STUN_ATTR_RESP_ORIGIN: return "RESPONSE-ORIGIN"; |
| 393 | case STUN_ATTR_OTHER_ADDR: return "OTHER-ADDR"; |
| 394 | default: return "???"; |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | |
| 399 | void stun_attr_dump(const struct stun_attr *a) |
| 400 | { |
| 401 | uint32_t i; |
| 402 | size_t len; |
| 403 | |
| 404 | if (!a) |
| 405 | return; |
| 406 | |
| 407 | (void)re_printf(" %-25s", stun_attr_name(a->type)); |
| 408 | |
| 409 | switch (a->type) { |
| 410 | |
| 411 | case STUN_ATTR_MAPPED_ADDR: |
| 412 | case STUN_ATTR_XOR_PEER_ADDR: |
| 413 | case STUN_ATTR_XOR_RELAY_ADDR: |
| 414 | case STUN_ATTR_XOR_MAPPED_ADDR: |
| 415 | case STUN_ATTR_ALT_SERVER: |
| 416 | case STUN_ATTR_RESP_ORIGIN: |
| 417 | case STUN_ATTR_OTHER_ADDR: |
| 418 | (void)re_printf("%J", &a->v.sa); |
| 419 | break; |
| 420 | |
| 421 | case STUN_ATTR_CHANGE_REQ: |
| 422 | (void)re_printf("ip=%u port=%u", a->v.change_req.ip, |
| 423 | a->v.change_req.port); |
| 424 | break; |
| 425 | |
| 426 | case STUN_ATTR_USERNAME: |
| 427 | case STUN_ATTR_REALM: |
| 428 | case STUN_ATTR_NONCE: |
| 429 | case STUN_ATTR_SOFTWARE: |
| 430 | (void)re_printf("%s", a->v.str); |
| 431 | break; |
| 432 | |
| 433 | case STUN_ATTR_MSG_INTEGRITY: |
| 434 | (void)re_printf("%w", a->v.msg_integrity, |
| 435 | sizeof(a->v.msg_integrity)); |
| 436 | break; |
| 437 | |
| 438 | case STUN_ATTR_ERR_CODE: |
| 439 | (void)re_printf("%u %s", a->v.err_code.code, |
| 440 | a->v.err_code.reason); |
| 441 | break; |
| 442 | |
| 443 | case STUN_ATTR_UNKNOWN_ATTR: |
| 444 | for (i=0; i<a->v.unknown_attr.typec; i++) |
| 445 | (void)re_printf("0x%04x ", a->v.unknown_attr.typev[i]); |
| 446 | break; |
| 447 | |
| 448 | case STUN_ATTR_CHANNEL_NUMBER: |
| 449 | (void)re_printf("0x%04x", a->v.uint16); |
| 450 | break; |
| 451 | |
| 452 | case STUN_ATTR_LIFETIME: |
| 453 | case STUN_ATTR_PRIORITY: |
| 454 | (void)re_printf("%u", a->v.uint32); |
| 455 | break; |
| 456 | |
| 457 | case STUN_ATTR_DATA: |
| 458 | case STUN_ATTR_PADDING: |
| 459 | len = min(mbuf_get_left(&a->v.mb), 16); |
| 460 | (void)re_printf("%w%s (%zu bytes)", mbuf_buf(&a->v.mb), len, |
| 461 | mbuf_get_left(&a->v.mb) > 16 ? "..." : "", |
| 462 | mbuf_get_left(&a->v.mb)); |
| 463 | break; |
| 464 | |
| 465 | case STUN_ATTR_REQ_ADDR_FAMILY: |
| 466 | case STUN_ATTR_REQ_TRANSPORT: |
| 467 | (void)re_printf("%u", a->v.uint8); |
| 468 | break; |
| 469 | |
| 470 | case STUN_ATTR_EVEN_PORT: |
| 471 | (void)re_printf("r=%u", a->v.even_port.r); |
| 472 | break; |
| 473 | |
| 474 | case STUN_ATTR_DONT_FRAGMENT: |
| 475 | case STUN_ATTR_USE_CAND: |
| 476 | /* no value */ |
| 477 | break; |
| 478 | |
| 479 | case STUN_ATTR_RSV_TOKEN: |
| 480 | (void)re_printf("0x%016llx", a->v.rsv_token); |
| 481 | break; |
| 482 | |
| 483 | case STUN_ATTR_RESP_PORT: |
| 484 | (void)re_printf("%u", a->v.uint16); |
| 485 | break; |
| 486 | |
| 487 | case STUN_ATTR_FINGERPRINT: |
| 488 | (void)re_printf("0x%08x", a->v.fingerprint); |
| 489 | break; |
| 490 | |
| 491 | case STUN_ATTR_CONTROLLING: |
| 492 | case STUN_ATTR_CONTROLLED: |
| 493 | (void)re_printf("%llu", a->v.uint64); |
| 494 | break; |
| 495 | |
| 496 | default: |
| 497 | (void)re_printf("???"); |
| 498 | break; |
| 499 | } |
| 500 | |
| 501 | (void)re_printf("\n"); |
| 502 | } |