int agent_alloc(struct agent **agp, struct reicec *cli, const struct trice_conf *conf) { struct agent *ag; int err = 0; ag = mem_zalloc(sizeof(*ag), destructor); if (!ag) return ENOMEM; ag->cli = cli; ag->client = cli->client; rand_str(ag->lufrag, sizeof(ag->lufrag)); rand_str(ag->lpwd, sizeof(ag->lpwd)); err = trice_alloc(&ag->icem, conf, cli->client, ag->lufrag, ag->lpwd); if (err) goto out; err = stun_alloc(&ag->stun, NULL, NULL, NULL); if (err) goto out; out: if (err) mem_deref(ag); else *agp = ag; return err; }
/** * Add a new ICE Media object to the ICE Session * * @param icemp Pointer to allocated ICE Media object * @param ice ICE Session * @param proto Transport protocol * @param layer Protocol stack layer * @param gh Gather handler * @param chkh Connectivity check handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int icem_alloc(struct icem **icemp, struct ice *ice, int proto, int layer, ice_gather_h *gh, ice_connchk_h *chkh, void *arg) { struct icem *icem; int err = 0; if (!ice) return EINVAL; if (proto != IPPROTO_UDP) return EPROTONOSUPPORT; icem = mem_zalloc(sizeof(*icem), icem_destructor); if (!icem) return ENOMEM; tmr_init(&icem->tmr_pace); list_init(&icem->lcandl); list_init(&icem->rcandl); list_init(&icem->checkl); list_init(&icem->validl); icem->ice = ice; icem->layer = layer; icem->proto = proto; icem->state = CHECKLIST_NULL; icem->nstun = 0; icem->gh = gh; icem->chkh = chkh; icem->arg = arg; if (ICE_MODE_FULL == ice->lmode) { err = stun_alloc(&icem->stun, NULL, NULL, NULL); if (err) goto out; /* Update STUN Transport */ stun_conf(icem->stun)->rto = ice->conf.rto; stun_conf(icem->stun)->rc = ice->conf.rc; } if (err) goto out; list_append(&ice->ml, &icem->le, icem); out: if (err) mem_deref(icem); else if (icemp) *icemp = icem; return err; }
/** * Allocate a SIP stack instance * * @param sipp Pointer to allocated SIP stack * @param dnsc DNS Client (optional) * @param ctsz Size of client transactions hashtable (power of 2) * @param stsz Size of server transactions hashtable (power of 2) * @param tcsz Size of SIP transport hashtable (power of 2) * @param software Software identifier * @param exith SIP-stack exit handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sip_alloc(struct sip **sipp, struct dnsc *dnsc, uint32_t ctsz, uint32_t stsz, uint32_t tcsz, const char *software, sip_exit_h *exith, void *arg) { struct sip *sip; int err; if (!sipp) return EINVAL; sip = mem_zalloc(sizeof(*sip), destructor); if (!sip) return ENOMEM; err = sip_transp_init(sip, tcsz); if (err) goto out; err = sip_ctrans_init(sip, ctsz); if (err) goto out; err = sip_strans_init(sip, stsz); if (err) goto out; err = hash_alloc(&sip->ht_udpconn, tcsz); if (err) goto out; err = stun_alloc(&sip->stun, NULL, NULL, NULL); if (err) goto out; if (software) { err = str_dup(&sip->software, software); if (err) goto out; } sip->dnsc = mem_ref(dnsc); sip->exith = exith; sip->arg = arg; out: if (err) mem_deref(sip); else *sipp = sip; return err; }
/** * Allocate a new ICE Session * * @param icep Pointer to allocated ICE Session object * @param mode ICE Mode; Full-mode or Lite-mode * @param offerer True if we are SDP offerer, otherwise false * * @return 0 if success, otherwise errorcode */ int ice_alloc(struct ice **icep, enum ice_mode mode, bool offerer) { struct ice *ice; int err = 0; if (!icep) return EINVAL; ice = mem_zalloc(sizeof(*ice), ice_destructor); if (!ice) return ENOMEM; list_init(&ice->ml); ice->conf = conf_default; ice->lmode = mode; ice->tiebrk = rand_u64(); rand_str(ice->lufrag, sizeof(ice->lufrag)); rand_str(ice->lpwd, sizeof(ice->lpwd)); ice_determine_role(ice, offerer); if (ICE_MODE_FULL == ice->lmode) { err = stun_alloc(&ice->stun, NULL, NULL, NULL); if (err) goto out; /* Update STUN Transport */ stun_conf(ice->stun)->rto = ice->conf.rto; stun_conf(ice->stun)->rc = ice->conf.rc; } out: if (err) mem_deref(ice); else *icep = ice; return err; }
/** * Allocate a new NAT Hairpinning discovery session * * @param nhp Pointer to allocated NAT Hairpinning object * @param proto Transport protocol * @param srv STUN Server IP address and port number * @param proto Transport protocol * @param conf STUN configuration (Optional) * @param hph Hairpinning result handler * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_hairpinning_alloc(struct nat_hairpinning **nhp, const struct sa *srv, int proto, const struct stun_conf *conf, nat_hairpinning_h *hph, void *arg) { struct nat_hairpinning *nh; struct sa local; int err; if (!srv || !hph) return EINVAL; nh = mem_zalloc(sizeof(*nh), hairpinning_destructor); if (!nh) return ENOMEM; err = stun_alloc(&nh->stun, conf, NULL, NULL); if (err) goto out; sa_cpy(&nh->srv, srv); nh->proto = proto; nh->hph = hph; nh->arg = arg; switch (proto) { case IPPROTO_UDP: err = udp_listen(&nh->us, NULL, udp_recv_handler, nh); break; case IPPROTO_TCP: sa_set_in(&local, 0, 0); /* * Part I - Allocate and bind all sockets */ err = tcp_sock_alloc(&nh->ts, &local, tcp_conn_handler, nh); if (err) break; err = tcp_conn_alloc(&nh->tc, &nh->srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, nh); if (err) break; err = tcp_sock_bind(nh->ts, &local); if (err) break; err = tcp_sock_local_get(nh->ts, &local); if (err) break; err = tcp_conn_bind(nh->tc, &local); if (err) break; /* * Part II - Listen and connect all sockets */ err = tcp_sock_listen(nh->ts, 5); break; default: err = EPROTONOSUPPORT; break; } out: if (err) mem_deref(nh); else *nhp = nh; return err; }
/** * Allocate a TURN Client * * @param turncp Pointer to allocated TURN Client * @param conf Optional STUN Configuration * @param proto Transport Protocol * @param sock Transport socket * @param layer Transport layer * @param srv TURN Server IP-address * @param username Authentication username * @param password Authentication password * @param lifetime Allocate lifetime in [seconds] * @param th TURN handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int turnc_alloc(struct turnc **turncp, const struct stun_conf *conf, int proto, void *sock, int layer, const struct sa *srv, const char *username, const char *password, uint32_t lifetime, turnc_h *th, void *arg) { struct turnc *turnc; int err; if (!turncp || !sock || !srv || !username || !password || !th) return EINVAL; turnc = mem_zalloc(sizeof(*turnc), destructor); if (!turnc) return ENOMEM; err = stun_alloc(&turnc->stun, conf, NULL, NULL); if (err) goto out; err = str_dup(&turnc->username, username); if (err) goto out; err = str_dup(&turnc->password, password); if (err) goto out; err = turnc_perm_hash_alloc(&turnc->perms, PERM_HASH_SIZE); if (err) goto out; err = turnc_chan_hash_alloc(&turnc->chans, CHAN_HASH_SIZE); if (err) goto out; tmr_init(&turnc->tmr); turnc->proto = proto; turnc->sock = mem_ref(sock); turnc->psrv = *srv; turnc->srv = *srv; turnc->lifetime = lifetime; turnc->th = th; turnc->arg = arg; switch (proto) { case IPPROTO_UDP: err = udp_register_helper(&turnc->uh, sock, layer, udp_send_handler, udp_recv_handler, turnc); break; default: err = 0; break; } if (err) goto out; err = allocate_request(turnc); if (err) goto out; out: if (err) mem_deref(turnc); else *turncp = turnc; return err; }
/** * Add a new ICE Media object to the ICE Session * * @param icemp Pointer to allocated ICE Media object * @param mode ICE mode * @param role Local ICE role * @param proto Transport protocol * @param layer Protocol stack layer * @param tiebrk Tie-breaker value, must be same for all media streams * @param lufrag Local username fragment * @param lpwd Local password * @param chkh Connectivity check handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int icem_alloc(struct icem **icemp, enum ice_mode mode, enum ice_role role, int proto, int layer, uint64_t tiebrk, const char *lufrag, const char *lpwd, ice_connchk_h *chkh, void *arg) { struct icem *icem; int err = 0; if (!icemp || !tiebrk || !lufrag || !lpwd) return EINVAL; if (str_len(lufrag) < 4 || str_len(lpwd) < 22) { DEBUG_WARNING("alloc: lufrag/lpwd is too short\n"); return EINVAL; } if (proto != IPPROTO_UDP) return EPROTONOSUPPORT; icem = mem_zalloc(sizeof(*icem), icem_destructor); if (!icem) return ENOMEM; icem->conf = conf_default; tmr_init(&icem->tmr_pace); list_init(&icem->lcandl); list_init(&icem->rcandl); list_init(&icem->checkl); list_init(&icem->validl); icem->layer = layer; icem->proto = proto; icem->state = ICE_CHECKLIST_NULL; icem->chkh = chkh; icem->arg = arg; if (err) goto out; icem->lmode = mode; icem->tiebrk = tiebrk; err |= str_dup(&icem->lufrag, lufrag); err |= str_dup(&icem->lpwd, lpwd); if (err) goto out; ice_determine_role(icem, role); if (ICE_MODE_FULL == icem->lmode) { err = stun_alloc(&icem->stun, NULL, NULL, NULL); if (err) goto out; /* Update STUN Transport */ stun_conf(icem->stun)->rto = icem->conf.rto; stun_conf(icem->stun)->rc = icem->conf.rc; } out: if (err) mem_deref(icem); else if (icemp) *icemp = icem; return err; }
/** * Allocate a new NAT Mapping Behaviour Discovery session * * @param nmp Pointer to allocated NAT Mapping object * @param laddr Local IP address * @param srv STUN Server IP address and port * @param proto Transport protocol * @param conf STUN configuration (Optional) * @param mh Mapping handler * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_mapping_alloc(struct nat_mapping **nmp, const struct sa *laddr, const struct sa *srv, int proto, const struct stun_conf *conf, nat_mapping_h *mh, void *arg) { struct nat_mapping *nm; int i, err; if (!nmp || !laddr || !srv || !mh) return EINVAL; nm = mem_zalloc(sizeof(*nm), mapping_destructor); if (!nm) return ENOMEM; err = stun_alloc(&nm->stun, conf, NULL, NULL); if (err) goto out; nm->proto = proto; sa_cpy(&nm->laddr, laddr); switch (proto) { case IPPROTO_UDP: err = udp_listen(&nm->us, &nm->laddr, udp_recv_handler, nm); if (err) goto out; err = udp_local_get(nm->us, &nm->laddr); if (err) goto out; break; case IPPROTO_TCP: /* Allocate and bind 3 TCP Sockets */ for (i=0; i<3; i++) { err = tcp_conn_alloc(&nm->tcv[i], srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, nm); if (err) goto out; err = tcp_conn_bind(nm->tcv[i], &nm->laddr); if (err) goto out; err = tcp_conn_local_get(nm->tcv[i], &nm->laddr); if (err) goto out; } break; default: err = EPROTONOSUPPORT; goto out; } sa_cpy(&nm->srv, srv); nm->mh = mh; nm->arg = arg; *nmp = nm; out: if (err) mem_deref(nm); return err; }
static int test_stun_request(int proto, bool natted) { struct stunserver *srv = NULL; struct stun_ctrans *ct = NULL; struct nat *nat = NULL; struct test test; struct sa laddr, public_addr; int err; memset(&test, 0, sizeof(test)); err = stunserver_alloc(&srv); if (err) goto out; err = stun_alloc(&test.stun, NULL, NULL, NULL); if (err) goto out; if (proto == IPPROTO_UDP) { err = sa_set_str(&laddr, "127.0.0.1", 0); TEST_ERR(err); err = udp_listen(&test.us, &laddr, udp_recv_handler, &test); if (err) goto out; err = udp_local_get(test.us, &laddr); TEST_ERR(err); } if (natted) { err = sa_set_str(&public_addr, "4.5.6.7", 0); TEST_ERR(err); err = nat_alloc(&nat, srv->us, &public_addr); if (err) goto out; sa_set_port(&public_addr, sa_port(&laddr)); } else { public_addr = laddr; } err = stun_request(&ct, test.stun, proto, test.us, stunserver_addr(srv, proto), 0, STUN_METHOD_BINDING, NULL, 0, true, stun_resp_handler, &test, 0); if (err) goto out; TEST_ASSERT(ct != NULL); err = re_main_timeout(100); if (err) goto out; if (srv->err) { err = srv->err; goto out; } if (test.err) { err = test.err; goto out; } /* verify results */ TEST_ASSERT(srv->nrecv >= 1); TEST_EQUALS(1, test.n_resp); if (proto == IPPROTO_UDP) { TEST_SACMP(&public_addr, &test.mapped_addr, SA_ALL); } out: mem_deref(test.stun); mem_deref(test.us); mem_deref(nat); mem_deref(srv); return err; }