diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-03-03 02:16:36 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-03-03 02:16:36 +0000 |
commit | fd91e3e6c398d5965e85a38bdf197af70ad86f75 (patch) | |
tree | 68aa6c97ef5c156ac6ddf53bd389b4be2be841b1 /pjlib-util/src | |
parent | 8a445dbdcb57138676aed4061b9cf1c3c1b1b10b (diff) |
Committed today's work on STUN
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1037 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib-util/src')
-rw-r--r-- | pjlib-util/src/pjlib-util/errno.c | 17 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_auth.c | 341 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_msg.c | 741 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_msg_dump.c | 16 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_server.c | 129 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_session.c | 334 | ||||
-rw-r--r-- | pjlib-util/src/pjlib-util/stun_transaction.c | 7 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-client/client_main.c | 51 | ||||
-rw-r--r-- | pjlib-util/src/pjstun-srv-test/server_main.c | 108 |
9 files changed, 900 insertions, 844 deletions
diff --git a/pjlib-util/src/pjlib-util/errno.c b/pjlib-util/src/pjlib-util/errno.c index 11e67c68..a60b3184 100644 --- a/pjlib-util/src/pjlib-util/errno.c +++ b/pjlib-util/src/pjlib-util/errno.c @@ -68,6 +68,23 @@ static const struct PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NXRRSET, "DNS \"The RRset (name, type) does not exist\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTAUTH, "DNS \"Not authorized\""), PJ_BUILD_ERR( PJLIB_UTIL_EDNS_NOTZONE, "DNS \"The zone specified is not a zone\""), + + /* STUN */ + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNTOOMANYATTR, "Too many STUN attributes"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNUNKNOWNATTR, "Unknown STUN attribute"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINADDRLEN, "Invalid STUN socket address length"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNIPV6NOTSUPP, "STUN IPv6 attribute not supported"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOTRESPONSE, "Expecting STUN response message"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNINVALIDID, "STUN transaction ID mismatch"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOHANDLER, "Unable to find STUN handler for the request"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNMSGINTPOS, "Found non-FINGERPRINT attr. after MESSAGE-INTEGRITY"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNFINGERPOS, "Found STUN attribute after FINGERPRINT"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOUSERNAME, "Missing STUN USERNAME attribute"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNMSGINT, "Missing/invalid STUN MESSAGE-INTEGRITY attribute"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNDUPATTR, "Found duplicate STUN attribute"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNOREALM, "Missing STUN REALM attribute"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNNONCE, "Missing/stale STUN NONCE attribute value"), + PJ_BUILD_ERR( PJLIB_UTIL_ESTUNTSXFAILED, "STUN transaction terminates with failure"), }; #endif /* PJ_HAS_ERROR_STRING */ diff --git a/pjlib-util/src/pjlib-util/stun_auth.c b/pjlib-util/src/pjlib-util/stun_auth.c new file mode 100644 index 00000000..abf61b6e --- /dev/null +++ b/pjlib-util/src/pjlib-util/stun_auth.c @@ -0,0 +1,341 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjlib-util/stun_auth.h> +#include <pjlib-util/errno.h> +#include <pjlib-util/hmac_sha1.h> +#include <pjlib-util/sha1.h> +#include <pj/assert.h> +#include <pj/string.h> + + +/* Duplicate credential */ +PJ_DEF(void) pj_stun_auth_cred_dup( pj_pool_t *pool, + pj_stun_auth_cred *dst, + const pj_stun_auth_cred *src) +{ + dst->type = src->type; + + switch (src->type) { + case PJ_STUN_AUTH_CRED_STATIC: + pj_strdup(pool, &dst->data.static_cred.realm, + &src->data.static_cred.realm); + pj_strdup(pool, &dst->data.static_cred.username, + &src->data.static_cred.username); + dst->data.static_cred.data_type = src->data.static_cred.data_type; + pj_strdup(pool, &dst->data.static_cred.data, + &src->data.static_cred.data); + pj_strdup(pool, &dst->data.static_cred.nonce, + &src->data.static_cred.nonce); + break; + case PJ_STUN_AUTH_CRED_DYNAMIC: + pj_memcpy(&dst->data.dyn_cred, &src->data.dyn_cred, + sizeof(src->data.dyn_cred)); + break; + } +} + + +PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos) +{ + return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]); +} + + +/* Send 401 response */ +static pj_status_t create_challenge(pj_pool_t *pool, + const pj_stun_msg *msg, + int err_code, + const pj_str_t *err_msg, + const pj_str_t *realm, + const pj_str_t *nonce, + pj_stun_msg **p_response) +{ + pj_stun_msg *response; + pj_str_t tmp_nonce; + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + err_code, err_msg, &response); + if (rc != PJ_SUCCESS) + return rc; + + + if (realm && realm->slen) { + rc = pj_stun_msg_add_string_attr(pool, response, + PJ_STUN_ATTR_REALM, + realm); + if (rc != PJ_SUCCESS) + return rc; + + /* long term must include nonce */ + if (!nonce || nonce->slen == 0) { + tmp_nonce = pj_str("pjstun"); + nonce = &tmp_nonce; + } + } + + if (nonce && nonce->slen) { + rc = pj_stun_msg_add_string_attr(pool, response, + PJ_STUN_ATTR_NONCE, + nonce); + if (rc != PJ_SUCCESS) + return rc; + } + + *p_response = response; + + return PJ_SUCCESS; +} + + +/* Verify credential */ +PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + pj_stun_auth_cred *cred, + pj_pool_t *pool, + pj_stun_msg **p_response) +{ + pj_str_t realm, nonce, password; + const pj_stun_msgint_attr *amsgi; + unsigned amsgi_pos; + const pj_stun_username_attr *auser; + pj_bool_t username_ok; + const pj_stun_realm_attr *arealm; + const pj_stun_realm_attr *anonce; + pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; + pj_uint8_t md5_digest[16]; + pj_str_t key; + pj_status_t status; + + /* msg and credential MUST be specified */ + PJ_ASSERT_RETURN(pkt && pkt_len && msg && cred, PJ_EINVAL); + + /* If p_response is specified, pool MUST be specified. */ + PJ_ASSERT_RETURN(!p_response || pool, PJ_EINVAL); + + if (p_response) + *p_response = NULL; + + if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) + p_response = NULL; + + /* Get realm and nonce */ + realm.slen = nonce.slen = 0; + if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { + realm = cred->data.static_cred.realm; + nonce = cred->data.static_cred.nonce; + } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { + status = cred->data.dyn_cred.get_auth(cred->data.dyn_cred.user_data, + pool, &realm, &nonce); + if (status != PJ_SUCCESS) + return status; + } else { + pj_assert(!"Unexpected"); + return PJ_EBUG; + } + + /* First check that MESSAGE-INTEGRITY is present */ + amsgi = (const pj_stun_msgint_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); + if (amsgi == NULL) { + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_UNAUTHORIZED, NULL, + &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNMSGINT; + } + + /* Next check that USERNAME is present */ + auser = (const pj_stun_username_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); + if (auser == NULL) { + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_USERNAME, NULL, + &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNNOUSERNAME; + } + + /* Get REALM, if any */ + arealm = (const pj_stun_realm_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); + + /* Check if username match */ + if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { + username_ok = !pj_strcmp(&auser->value, + &cred->data.static_cred.username); + password = cred->data.static_cred.data; + } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { + int data_type = 0; + pj_status_t rc; + rc = cred->data.dyn_cred.get_password(cred->data.dyn_cred.user_data, + (arealm?&arealm->value:NULL), + &auser->value, pool, + &data_type, &password); + username_ok = (rc == PJ_SUCCESS); + } else { + username_ok = PJ_TRUE; + password.slen = 0; + } + + if (!username_ok) { + /* Username mismatch */ + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_UNKNOWN_USERNAME, NULL, + &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNUSERNAME; + } + + + /* Get NONCE attribute */ + anonce = (pj_stun_nonce_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_NONCE, 0); + + /* Check for long term/short term requirements. */ + if (realm.slen != 0 && arealm == NULL) { + /* Long term credential is required and REALM is not present */ + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, NULL, + &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNNOREALM; + + } else if (realm.slen != 0 && arealm != NULL) { + /* We want long term, and REALM is present */ + + /* NONCE must be present. */ + if (anonce == NULL) { + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, + NULL, &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNNONCE; + } + + /* Verify REALM matches */ + if (pj_stricmp(&arealm->value, &realm)) { + /* REALM doesn't match */ + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, + NULL, &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNNOREALM; + } + + /* Valid case, will validate the message integrity later */ + + } else if (realm.slen == 0 && arealm != NULL) { + /* We want to use short term credential, but client uses long + * term credential. The draft doesn't mention anything about + * switching between long term and short term. + */ + + /* For now just accept the credential, anyway it will probably + * cause wrong message integrity value later. + */ + } else if (realm.slen==0 && arealm == NULL) { + /* Short term authentication is wanted, and one is supplied */ + + /* Application MAY request NONCE to be supplied */ + if (nonce.slen != 0) { + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, + NULL, &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNNONCE; + } + } + + /* If NONCE is present, validate it */ + if (anonce) { + pj_bool_t ok; + + if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { + ok=cred->data.dyn_cred.verify_nonce(cred->data.dyn_cred.user_data, + (arealm?&arealm->value:NULL), + &auser->value, + &anonce->value); + } else { + if (nonce.slen) { + ok = !pj_strcmp(&anonce->value, &nonce); + } else { + ok = PJ_TRUE; + } + } + + if (!ok) { + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_STALE_NONCE, + NULL, &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNNONCE; + } + } + + /* Get the position of MESSAGE-INTEGRITY in the packet */ + amsgi_pos = 20+msg->hdr.length-24; + if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { + /* Found MESSAGE-INTEGRITY as the last attribute */ + } else { + amsgi_pos = 0; + } + + if (amsgi_pos==0) { + amsgi_pos = 20+msg->hdr.length-8-24; + if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { + /* Found MESSAGE-INTEGRITY before FINGERPRINT */ + } else { + amsgi_pos = 0; + } + } + + if (amsgi_pos==0) { + pj_assert(!"Unable to find MESSAGE-INTEGRITY in the message!"); + return PJ_EBUG; + } + + /* Determine which key to use */ + if (realm.slen) { + pj_stun_calc_md5_key(md5_digest, &realm, &auser->value, &password); + key.ptr = (char*)md5_digest; + key.slen = 16; + } else { + key = password; + } + + /* Now calculate HMAC of the message */ + pj_hmac_sha1(pkt, amsgi_pos, (pj_uint8_t*)key.ptr, key.slen, digest); + + /* Compare HMACs */ + if (pj_memcmp(amsgi->hmac, digest, 20)) { + /* HMAC value mismatch */ + if (p_response) { + create_challenge(pool, msg, PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE, + NULL, &realm, &nonce, p_response); + } + return PJLIB_UTIL_ESTUNMSGINT; + } + + /* Everything looks okay! */ + return PJ_SUCCESS; +} + + diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index 6954434a..7c623ad4 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -21,7 +21,6 @@ #include <pjlib-util/errno.h> #include <pjlib-util/hmac_sha1.h> #include <pjlib-util/md5.h> -#include <pjlib-util/sha1.h> #include <pj/assert.h> #include <pj/log.h> #include <pj/os.h> @@ -88,37 +87,37 @@ struct attr_desc }; -static pj_status_t decode_generic_ip_addr_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr); -static pj_status_t encode_generic_ip_addr_attr(const void *a, pj_uint8_t *buf, - unsigned len, - unsigned *printed); -static pj_status_t decode_generic_string_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr); -static pj_status_t encode_generic_string_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed); -static pj_status_t decode_msg_integrity_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr); -static pj_status_t encode_msg_integrity_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed); -static pj_status_t decode_error_code_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr); -static pj_status_t encode_error_code_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed); +static pj_status_t decode_ip_addr_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr); +static pj_status_t encode_ip_addr_attr(const void *a, pj_uint8_t *buf, + unsigned len, + unsigned *printed); +static pj_status_t decode_string_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr); +static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed); +static pj_status_t decode_msgint_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr); +static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed); +static pj_status_t decode_errcode_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr); +static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed); static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); -static pj_status_t decode_generic_uint_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr); -static pj_status_t encode_generic_uint_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed); +static pj_status_t decode_uint_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr); +static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed); static pj_status_t decode_binary_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); @@ -142,56 +141,56 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_MAPPED_ADDR, */ "MAPPED-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_RESPONSE_ADDR, */ "RESPONSE-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_CHANGE_REQUEST, */ "CHANGE-REQUEST", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* PJ_STUN_ATTR_SOURCE_ADDR, */ "SOURCE-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_CHANGED_ADDR, */ "CHANGED-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_USERNAME, */ "USERNAME", - &decode_generic_string_attr, - &encode_generic_string_attr + &decode_string_attr, + &encode_string_attr }, { /* PJ_STUN_ATTR_PASSWORD, */ "PASSWORD", - &decode_generic_string_attr, - &encode_generic_string_attr + &decode_string_attr, + &encode_string_attr }, { /* PJ_STUN_ATTR_MESSAGE_INTEGRITY, */ "MESSAGE-INTEGRITY", - &decode_msg_integrity_attr, - &encode_msg_integrity_attr + &decode_msgint_attr, + &encode_msgint_attr }, { /* PJ_STUN_ATTR_ERROR_CODE, */ "ERROR-CODE", - &decode_error_code_attr, - &encode_error_code_attr + &decode_errcode_attr, + &encode_errcode_attr }, { /* PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, */ @@ -202,8 +201,8 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_REFLECTED_FROM, */ "REFLECTED-FROM", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* ID 0x000C is not assigned */ @@ -214,8 +213,8 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_LIFETIME, */ "LIFETIME", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* ID 0x000E is not assigned */ @@ -232,8 +231,8 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_BANDWIDTH, */ "BANDWIDTH", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* ID 0x0011 is not assigned */ @@ -244,8 +243,8 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_REMOTE_ADDRESS, */ "REMOTE-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_DATA, */ @@ -256,38 +255,38 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_REALM, */ "REALM", - &decode_generic_string_attr, - &encode_generic_string_attr + &decode_string_attr, + &encode_string_attr }, { /* PJ_STUN_ATTR_NONCE, */ "NONCE", - &decode_generic_string_attr, - &encode_generic_string_attr + &decode_string_attr, + &encode_string_attr }, { /* PJ_STUN_ATTR_RELAY_ADDRESS, */ "RELAY-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_REQUESTED_ADDR_TYPE, */ "REQUESTED-ADDRESS-TYPE", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* PJ_STUN_ATTR_REQUESTED_PORT_PROPS, */ "REQUESTED-PORT-PROPS", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* PJ_STUN_ATTR_REQUESTED_TRANSPORT, */ "REQUESTED-TRANSPORT", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* ID 0x001A is not assigned */ @@ -328,32 +327,32 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, */ "XOR-MAPPED-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_TIMER_VAL, */ "TIMER-VAL", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* PJ_STUN_ATTR_REQUESTED_IP, */ "REQUESTED-IP", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */ "XOR-REFLECTED-FROM", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_PRIORITY, */ "PRIORITY", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* PJ_STUN_ATTR_USE_CANDIDATE, */ @@ -364,8 +363,8 @@ struct attr_desc mandatory_attr_desc[] = { /* PJ_STUN_ATTR_XOR_INTERNAL_ADDR, */ "XOR-INTERNAL-ADDRESS", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, /* Sentinel */ @@ -382,26 +381,26 @@ static struct attr_desc extended_attr_desc[] = { /* PJ_STUN_ATTR_FINGERPRINT, */ "FINGERPRINT", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, { /* PJ_STUN_ATTR_SERVER, */ "SERVER", - &decode_generic_string_attr, - &encode_generic_string_attr + &decode_string_attr, + &encode_string_attr }, { /* PJ_STUN_ATTR_ALTERNATE_SERVER, */ "ALTERNATE-SERVER", - &decode_generic_ip_addr_attr, - &encode_generic_ip_addr_attr + &decode_ip_addr_attr, + &encode_ip_addr_attr }, { /* PJ_STUN_ATTR_REFRESH_INTERVAL, */ "REFRESH-INTERVAL", - &decode_generic_uint_attr, - &encode_generic_uint_attr + &decode_uint_attr, + &encode_uint_attr }, }; @@ -514,20 +513,20 @@ PJ_DEF(pj_str_t) pj_stun_get_err_reason(int err_code) * Create a generic STUN IP address attribute for IPv4 address. */ PJ_DEF(pj_status_t) -pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool, - int attr_type, - pj_bool_t xor_ed, - const pj_sockaddr_t *addr, - unsigned addr_len, - pj_stun_generic_ip_addr_attr **p_attr) +pj_stun_ip_addr_attr_create(pj_pool_t *pool, + int attr_type, + pj_bool_t xor_ed, + const pj_sockaddr_t *addr, + unsigned addr_len, + pj_stun_ip_addr_attr **p_attr) { - pj_stun_generic_ip_addr_attr *attr; + pj_stun_ip_addr_attr *attr; PJ_ASSERT_RETURN(pool && addr_len && addr && p_attr, PJ_EINVAL); PJ_ASSERT_RETURN(addr_len == sizeof(pj_sockaddr_in) || addr_len == sizeof(pj_sockaddr_in6), PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_ip_addr_attr); INIT_ATTR(attr, attr_type, STUN_GENERIC_IP_ADDR_LEN); if (!xor_ed) { @@ -555,17 +554,17 @@ pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool, * Create and add generic STUN IP address attribute to a STUN message. */ PJ_DEF(pj_status_t) -pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool, - pj_stun_msg *msg, - int attr_type, - pj_bool_t xor_ed, - const pj_sockaddr_t *addr, - unsigned addr_len) +pj_stun_msg_add_ip_addr_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_bool_t xor_ed, + const pj_sockaddr_t *addr, + unsigned addr_len) { - pj_stun_generic_ip_addr_attr *attr; + pj_stun_ip_addr_attr *attr; pj_status_t status; - status = pj_stun_generic_ip_addr_attr_create(pool, attr_type, xor_ed, + status = pj_stun_ip_addr_attr_create(pool, attr_type, xor_ed, addr, addr_len, &attr); if (status != PJ_SUCCESS) return status; @@ -573,15 +572,15 @@ pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool, return pj_stun_msg_add_attr(msg, &attr->hdr); } -static pj_status_t decode_generic_ip_addr_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr) +static pj_status_t decode_ip_addr_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr) { - pj_stun_generic_ip_addr_attr *attr; + pj_stun_ip_addr_attr *attr; pj_uint32_t val; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_ip_addr_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_ip_addr_attr); pj_memcpy(attr, buf, ATTR_HDR_LEN); /* Convert to host byte order */ @@ -611,23 +610,23 @@ static pj_status_t decode_generic_ip_addr_attr(pj_pool_t *pool, } -static pj_status_t encode_generic_ip_addr_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed) +static pj_status_t encode_ip_addr_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed) { enum { ATTR_LEN = ATTR_HDR_LEN + STUN_GENERIC_IP_ADDR_LEN }; pj_uint8_t *start_buf = buf; - const pj_stun_generic_ip_addr_attr *ca = - (const pj_stun_generic_ip_addr_attr *)a; - pj_stun_generic_ip_addr_attr *attr; + const pj_stun_ip_addr_attr *ca = + (const pj_stun_ip_addr_attr *)a; + pj_stun_ip_addr_attr *attr; if (len < ATTR_LEN) return PJ_ETOOSMALL; /* Copy and convert headers to network byte order */ pj_memcpy(buf, a, ATTR_HDR_LEN); - attr = (pj_stun_generic_ip_addr_attr*) buf; + attr = (pj_stun_ip_addr_attr*) buf; attr->hdr.type = pj_htons(attr->hdr.type); attr->hdr.length = pj_htons((pj_uint16_t)STUN_GENERIC_IP_ADDR_LEN); buf += ATTR_HDR_LEN; @@ -665,16 +664,16 @@ static pj_status_t encode_generic_ip_addr_attr(const void *a, pj_uint8_t *buf, * Create a STUN generic string attribute. */ PJ_DEF(pj_status_t) -pj_stun_generic_string_attr_create(pj_pool_t *pool, - int attr_type, - const pj_str_t *value, - pj_stun_generic_string_attr **p_attr) +pj_stun_string_attr_create(pj_pool_t *pool, + int attr_type, + const pj_str_t *value, + pj_stun_string_attr **p_attr) { - pj_stun_generic_string_attr *attr; + pj_stun_string_attr *attr; PJ_ASSERT_RETURN(pool && value && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_string_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_string_attr); INIT_ATTR(attr, attr_type, value->slen); pj_strdup(pool, &attr->value, value); @@ -688,15 +687,15 @@ pj_stun_generic_string_attr_create(pj_pool_t *pool, * Create and add STUN generic string attribute to the message. */ PJ_DEF(pj_status_t) -pj_stun_msg_add_generic_string_attr(pj_pool_t *pool, - pj_stun_msg *msg, - int attr_type, - const pj_str_t *value) +pj_stun_msg_add_string_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + const pj_str_t *value) { - pj_stun_generic_string_attr *attr; + pj_stun_string_attr *attr; pj_status_t status; - status = pj_stun_generic_string_attr_create(pool, attr_type, value, + status = pj_stun_string_attr_create(pool, attr_type, value, &attr); if (status != PJ_SUCCESS) return status; @@ -705,15 +704,15 @@ pj_stun_msg_add_generic_string_attr(pj_pool_t *pool, } -static pj_status_t decode_generic_string_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr) +static pj_status_t decode_string_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr) { - pj_stun_generic_string_attr *attr; + pj_stun_string_attr *attr; pj_str_t value; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_string_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_string_attr); /* Copy the header */ pj_memcpy(attr, buf, ATTR_HDR_LEN); @@ -737,11 +736,11 @@ static pj_status_t decode_generic_string_attr(pj_pool_t *pool, } -static pj_status_t encode_generic_string_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed) +static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed) { - const pj_stun_generic_string_attr *ca = - (const pj_stun_generic_string_attr*)a; + const pj_stun_string_attr *ca = + (const pj_stun_string_attr*)a; pj_stun_attr_hdr *attr; /* Calculated total attr_len (add padding if necessary) */ @@ -856,16 +855,16 @@ static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, * Create a STUN generic 32bit value attribute. */ PJ_DEF(pj_status_t) -pj_stun_generic_uint_attr_create(pj_pool_t *pool, - int attr_type, - pj_uint32_t value, - pj_stun_generic_uint_attr **p_attr) +pj_stun_uint_attr_create(pj_pool_t *pool, + int attr_type, + pj_uint32_t value, + pj_stun_uint_attr **p_attr) { - pj_stun_generic_uint_attr *attr; + pj_stun_uint_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_uint_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_uint_attr); INIT_ATTR(attr, attr_type, STUN_UINT_LEN); attr->value = value; @@ -876,36 +875,36 @@ pj_stun_generic_uint_attr_create(pj_pool_t *pool, /* Create and add STUN generic 32bit value attribute to the message. */ PJ_DEF(pj_status_t) -pj_stun_msg_add_generic_uint_attr(pj_pool_t *pool, - pj_stun_msg *msg, - int attr_type, - pj_uint32_t value) +pj_stun_msg_add_uint_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_uint32_t value) { - pj_stun_generic_uint_attr *attr; + pj_stun_uint_attr *attr; pj_status_t status; - status = pj_stun_generic_uint_attr_create(pool, attr_type, value, &attr); + status = pj_stun_uint_attr_create(pool, attr_type, value, &attr); if (status != PJ_SUCCESS) return status; return pj_stun_msg_add_attr(msg, &attr->hdr); } -static pj_status_t decode_generic_uint_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr) +static pj_status_t decode_uint_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr) { enum { ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN }; - pj_stun_generic_uint_attr *attr; + pj_stun_uint_attr *attr; /* Check that the struct address is valid */ - pj_assert(sizeof(pj_stun_generic_uint_attr) == ATTR_LEN); + pj_assert(sizeof(pj_stun_uint_attr) == ATTR_LEN); /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_generic_uint_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_uint_attr); pj_memcpy(attr, buf, ATTR_LEN); /* Convert to host byte order */ @@ -924,21 +923,21 @@ static pj_status_t decode_generic_uint_attr(pj_pool_t *pool, } -static pj_status_t encode_generic_uint_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed) +static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed) { enum { ATTR_LEN = STUN_UINT_LEN + ATTR_HDR_LEN }; - pj_stun_generic_uint_attr *attr; + pj_stun_uint_attr *attr; if (len < ATTR_LEN) return PJ_ETOOSMALL; /* Copy and convert attribute to network byte order */ pj_memcpy(buf, a, ATTR_LEN); - attr = (pj_stun_generic_uint_attr*) buf; + attr = (pj_stun_uint_attr*) buf; attr->hdr.type = pj_htons(attr->hdr.type); pj_assert(attr->hdr.length == STUN_UINT_LEN); attr->hdr.length = pj_htons(STUN_UINT_LEN); @@ -961,14 +960,14 @@ static pj_status_t encode_generic_uint_attr(const void *a, pj_uint8_t *buf, * Create a STUN MESSAGE-INTEGRITY attribute. */ PJ_DEF(pj_status_t) -pj_stun_msg_integrity_attr_create(pj_pool_t *pool, - pj_stun_msg_integrity_attr **p_attr) +pj_stun_msgint_attr_create(pj_pool_t *pool, + pj_stun_msgint_attr **p_attr) { - pj_stun_msg_integrity_attr *attr; + pj_stun_msgint_attr *attr; PJ_ASSERT_RETURN(pool && p_attr, PJ_EINVAL); - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg_integrity_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msgint_attr); INIT_ATTR(attr, PJ_STUN_ATTR_MESSAGE_INTEGRITY, STUN_MSG_INTEGRITY_LEN); *p_attr = attr; @@ -977,22 +976,35 @@ pj_stun_msg_integrity_attr_create(pj_pool_t *pool, } -static pj_status_t decode_msg_integrity_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr) +PJ_DEF(pj_status_t) pj_stun_msg_add_msgint_attr(pj_pool_t *pool, + pj_stun_msg *msg) +{ + pj_stun_msgint_attr *attr; + pj_status_t status; + + status = pj_stun_msgint_attr_create(pool, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + +static pj_status_t decode_msgint_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr) { enum { ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN }; - pj_stun_msg_integrity_attr *attr; + pj_stun_msgint_attr *attr; /* Check that struct size is valid */ - pj_assert(sizeof(pj_stun_msg_integrity_attr)==ATTR_LEN); + pj_assert(sizeof(pj_stun_msgint_attr)==ATTR_LEN); /* Create attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg_integrity_attr); - pj_memcpy(attr, buf, sizeof(pj_stun_msg_integrity_attr)); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msgint_attr); + pj_memcpy(attr, buf, sizeof(pj_stun_msgint_attr)); attr->hdr.type = pj_ntohs(attr->hdr.type); attr->hdr.length = pj_ntohs(attr->hdr.length); @@ -1006,21 +1018,21 @@ static pj_status_t decode_msg_integrity_attr(pj_pool_t *pool, } -static pj_status_t encode_msg_integrity_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed) +static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed) { enum { ATTR_LEN = STUN_MSG_INTEGRITY_LEN + ATTR_HDR_LEN }; - pj_stun_msg_integrity_attr *attr; + pj_stun_msgint_attr *attr; if (len < ATTR_LEN) return PJ_ETOOSMALL; /* Copy and convert attribute to network byte order */ pj_memcpy(buf, a, ATTR_LEN); - attr = (pj_stun_msg_integrity_attr*) buf; + attr = (pj_stun_msgint_attr*) buf; attr->hdr.type = pj_htons(attr->hdr.type); pj_assert(attr->hdr.length == STUN_MSG_INTEGRITY_LEN); attr->hdr.length = pj_htons(STUN_MSG_INTEGRITY_LEN); @@ -1040,12 +1052,12 @@ static pj_status_t encode_msg_integrity_attr(const void *a, pj_uint8_t *buf, * Create a STUN ERROR-CODE attribute. */ PJ_DEF(pj_status_t) -pj_stun_error_code_attr_create(pj_pool_t *pool, - int err_code, - const pj_str_t *err_reason, - pj_stun_error_code_attr **p_attr) +pj_stun_errcode_attr_create(pj_pool_t *pool, + int err_code, + const pj_str_t *err_reason, + pj_stun_errcode_attr **p_attr) { - pj_stun_error_code_attr *attr; + pj_stun_errcode_attr *attr; char err_buf[80]; pj_str_t str; @@ -1061,7 +1073,7 @@ pj_stun_error_code_attr_create(pj_pool_t *pool, err_reason = &str; } - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_error_code_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_errcode_attr); INIT_ATTR(attr, PJ_STUN_ATTR_ERROR_CODE, 4+err_reason->slen); attr->err_class = (pj_uint8_t)(err_code / 100); attr->number = (pj_uint8_t) (err_code % 100); @@ -1073,15 +1085,31 @@ pj_stun_error_code_attr_create(pj_pool_t *pool, } -static pj_status_t decode_error_code_attr(pj_pool_t *pool, - const pj_uint8_t *buf, - void **p_attr) +PJ_DEF(pj_status_t) pj_stun_msg_add_errcode_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int err_code, + const pj_str_t *err_reason) +{ + pj_stun_errcode_attr *err_attr; + pj_status_t status; + + status = pj_stun_errcode_attr_create(pool, err_code, err_reason, + &err_attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &err_attr->hdr); +} + +static pj_status_t decode_errcode_attr(pj_pool_t *pool, + const pj_uint8_t *buf, + void **p_attr) { - pj_stun_error_code_attr *attr; + pj_stun_errcode_attr *attr; pj_str_t value; /* Create the attribute */ - attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_error_code_attr); + attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_errcode_attr); /* Copy the header */ pj_memcpy(attr, buf, ATTR_HDR_LEN + 4); @@ -1104,12 +1132,12 @@ static pj_status_t decode_error_code_attr(pj_pool_t *pool, } -static pj_status_t encode_error_code_attr(const void *a, pj_uint8_t *buf, - unsigned len, unsigned *printed) +static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, + unsigned len, unsigned *printed) { - const pj_stun_error_code_attr *ca = - (const pj_stun_error_code_attr*)a; - pj_stun_error_code_attr *attr; + const pj_stun_errcode_attr *ca = + (const pj_stun_errcode_attr*)a; + pj_stun_errcode_attr *attr; if (len < ATTR_HDR_LEN + 4 + (unsigned)ca->reason.slen) return PJ_ETOOSMALL; @@ -1118,7 +1146,7 @@ static pj_status_t encode_error_code_attr(const void *a, pj_uint8_t *buf, pj_memcpy(buf, ca, ATTR_HDR_LEN + 4); /* Update length */ - attr = (pj_stun_error_code_attr*) buf; + attr = (pj_stun_errcode_attr*) buf; attr->hdr.length = (pj_uint16_t)(4 + ca->reason.slen); /* Convert fiends to network byte order */ @@ -1507,7 +1535,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, { unsigned msg_type = req_msg->hdr.type; pj_stun_msg *response; - pj_stun_error_code_attr *err_attr; pj_status_t status; PJ_ASSERT_RETURN(pool && p_response, PJ_EINVAL); @@ -1529,13 +1556,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, /* Add error code attribute */ if (err_code) { - status = pj_stun_error_code_attr_create(pool, err_code, err_msg, - &err_attr); + status = pj_stun_msg_add_errcode_attr(pool, response, + err_code, err_msg); if (status != PJ_SUCCESS) { return status; } - - pj_stun_msg_add_attr(response, &err_attr->hdr); } *p_response = response; @@ -1702,7 +1727,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_STUN_STATUS_BAD_REQUEST, NULL, p_response); } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_BAD_REQUEST); + return PJLIB_UTIL_ESTUNDUPATTR; } has_msg_int = PJ_TRUE; @@ -1716,7 +1741,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_STUN_STATUS_BAD_REQUEST, NULL, p_response); } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_BAD_REQUEST); + return PJLIB_UTIL_ESTUNDUPATTR; } has_fingerprint = PJ_TRUE; } else { @@ -1730,7 +1755,8 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_STUN_STATUS_BAD_REQUEST, NULL, p_response); } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_BAD_REQUEST); + return has_fingerprint ? PJLIB_UTIL_ESTUNFINGERPOS : + PJLIB_UTIL_ESTUNMSGINTPOS; } } @@ -1766,10 +1792,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, /* Calculate HMAC-SHA1 key for long term credential, by getting * MD5 digest of username, realm, and password. */ -static void calc_md5_key(pj_uint8_t digest[16], - const pj_str_t *realm, - const pj_str_t *username, - const pj_str_t *passwd) +void pj_stun_calc_md5_key(pj_uint8_t digest[16], + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *passwd) { /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking * the MD5 hash of the result of concatenating the following five @@ -1827,7 +1853,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, pj_uint8_t *start = buf; pj_stun_realm_attr *arealm = NULL; pj_stun_username_attr *auname = NULL; - pj_stun_msg_integrity_attr *amsg_integrity = NULL; + pj_stun_msgint_attr *amsgint = NULL; pj_stun_fingerprint_attr *afingerprint = NULL; unsigned printed; pj_status_t status; @@ -1859,8 +1885,8 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, const pj_stun_attr_hdr *attr_hdr = msg->attr[i]; if (attr_hdr->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { - pj_assert(amsg_integrity == NULL); - amsg_integrity = (pj_stun_msg_integrity_attr*) attr_hdr; + pj_assert(amsgint == NULL); + amsgint = (pj_stun_msgint_attr*) attr_hdr; /* Stop when encountering MESSAGE-INTEGRITY */ break; @@ -1901,9 +1927,9 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, if (attr_hdr->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { /* There mustn't be MESSAGE-INTEGRITY before */ - PJ_ASSERT_RETURN(amsg_integrity == NULL, + PJ_ASSERT_RETURN(amsgint == NULL, PJLIB_UTIL_ESTUNMSGINTPOS); - amsg_integrity = (pj_stun_msg_integrity_attr*) attr_hdr; + amsgint = (pj_stun_msgint_attr*) attr_hdr; } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) { afingerprint = (pj_stun_fingerprint_attr*) attr_hdr; @@ -1914,9 +1940,9 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, * calculating MESSAGE-INTEGRITY and FINGERPRINT. * Note that length is not including the 20 bytes header. */ - if (amsg_integrity && afingerprint) { + if (amsgint && afingerprint) { msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24 + 8); - } else if (amsg_integrity) { + } else if (amsgint) { msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24); } else if (afingerprint) { msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 8); @@ -1929,7 +1955,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, start[3] = (pj_uint8_t)(msg->hdr.length & 0x00FF); /* Calculate message integrity, if present */ - if (amsg_integrity != NULL) { + if (amsgint != NULL) { pj_uint8_t md5_key_buf[16]; pj_str_t key; @@ -1968,8 +1994,8 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, key = *password; } else { - calc_md5_key(md5_key_buf, &arealm->value, &auname->value, - password); + pj_stun_calc_md5_key(md5_key_buf, &arealm->value, + &auname->value, password); key.ptr = (char*) md5_key_buf; key.slen = 16; } @@ -1977,11 +2003,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, /* Calculate HMAC-SHA1 digest */ pj_hmac_sha1((pj_uint8_t*)buf, buf-start, (pj_uint8_t*)key.ptr, key.slen, - amsg_integrity->hmac); + amsgint->hmac); /* Put this attribute in the message */ - status = encode_msg_integrity_attr(amsg_integrity, buf, buf_size, - &printed); + status = encode_msgint_attr(amsgint, buf, buf_size, + &printed); if (status != PJ_SUCCESS) return status; @@ -1995,7 +2021,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, afingerprint->value ^= STUN_XOR_FINGERPRINT; /* Put this attribute in the message */ - status = encode_generic_uint_attr(afingerprint, buf, buf_size, + status = encode_uint_attr(afingerprint, buf, buf_size, &printed); if (status != PJ_SUCCESS) return status; @@ -2024,296 +2050,9 @@ PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg, for (; index < msg->attr_count; ++index) { if (msg->attr[index]->type == attr_type) - return (pj_stun_attr_hdr*) &msg->attr[index]; + return (pj_stun_attr_hdr*) msg->attr[index]; } return NULL; } - -/**************************************************************************/ -/* - * Authentication - */ - - -/* Send 401 response */ -static pj_status_t create_challenge(pj_pool_t *pool, - const pj_stun_msg *msg, - int err_code, - const pj_str_t *err_msg, - const pj_str_t *realm, - const pj_str_t *nonce, - pj_stun_msg **p_response) -{ - pj_stun_msg *response; - pj_status_t rc; - - rc = pj_stun_msg_create_response(pool, msg, - err_code, err_msg, &response); - if (rc != PJ_SUCCESS) - return rc; - - - if (realm && realm->slen) { - rc = pj_stun_msg_add_generic_string_attr(pool, response, - PJ_STUN_ATTR_REALM, - realm); - if (rc != PJ_SUCCESS) - return rc; - } - - if (nonce && nonce->slen) { - rc = pj_stun_msg_add_generic_string_attr(pool, response, - PJ_STUN_ATTR_NONCE, - nonce); - if (rc != PJ_SUCCESS) - return rc; - } - - *p_response = response; - - return PJ_SUCCESS; -} - -/* Verify credential */ -PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - pj_stun_auth_policy *pol, - pj_pool_t *pool, - pj_stun_msg **p_response) -{ - pj_str_t realm, nonce, password; - const pj_stun_msg_integrity_attr *amsgi; - unsigned amsgi_pos; - const pj_stun_username_attr *auser; - pj_bool_t username_ok; - const pj_stun_realm_attr *arealm; - const pj_stun_realm_attr *anonce; - pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; - pj_uint8_t md5_digest[16]; - pj_str_t key; - pj_status_t status; - - /* msg and policy MUST be specified */ - PJ_ASSERT_RETURN(pkt && pkt_len && msg && pol, PJ_EINVAL); - - /* If p_response is specified, pool MUST be specified. */ - PJ_ASSERT_RETURN(!p_response || pool, PJ_EINVAL); - - if (p_response) - *p_response = NULL; - - if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) - p_response = NULL; - - /* Get realm and nonce */ - realm.slen = nonce.slen = 0; - if (pol->type == PJ_STUN_POLICY_STATIC_SHORT_TERM) { - realm.slen = 0; - nonce = pol->data.static_short_term.nonce; - } else if (pol->type == PJ_STUN_POLICY_STATIC_LONG_TERM) { - realm = pol->data.static_long_term.realm; - nonce = pol->data.static_long_term.nonce; - } else if (pol->type == PJ_STUN_POLICY_DYNAMIC) { - status = pol->data.dynamic.get_auth(pol->user_data, pool, - &realm, &nonce); - if (status != PJ_SUCCESS) - return status; - } else { - pj_assert(!"Unexpected"); - return PJ_EBUG; - } - - /* First check that MESSAGE-INTEGRITY is present */ - amsgi = (const pj_stun_msg_integrity_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); - if (amsgi == NULL) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_UNAUTHORIZED, NULL, - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNAUTHORIZED); - } - - /* Next check that USERNAME is present */ - auser = (const pj_stun_username_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); - if (auser == NULL) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_USERNAME, NULL, - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_USERNAME); - } - - /* Get REALM, if any */ - arealm = (const pj_stun_realm_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); - - /* Check if username match */ - if (pol->type == PJ_STUN_POLICY_STATIC_SHORT_TERM) { - username_ok = !pj_strcmp(&auser->value, - &pol->data.static_short_term.username); - password = pol->data.static_short_term.password; - } else if (pol->type == PJ_STUN_POLICY_STATIC_LONG_TERM) { - username_ok = !pj_strcmp(&auser->value, - &pol->data.static_long_term.username); - password = pol->data.static_long_term.password; - } else if (pol->type == PJ_STUN_POLICY_DYNAMIC) { - pj_status_t rc; - rc = pol->data.dynamic.get_password(pol->user_data, - (arealm?&arealm->value:NULL), - &auser->value, pool, - &password); - username_ok = (rc == PJ_SUCCESS); - } else { - username_ok = PJ_TRUE; - password.slen = 0; - } - - if (!username_ok) { - /* Username mismatch */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_UNKNOWN_USERNAME, NULL, - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNKNOWN_USERNAME); - } - - - /* Get NONCE attribute */ - anonce = (pj_stun_nonce_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_NONCE, 0); - - /* Check for long term/short term requirements. */ - if (realm.slen != 0 && arealm == NULL) { - /* Long term credential is required and REALM is not present */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, NULL, - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); - - } else if (realm.slen != 0 && arealm != NULL) { - /* We want long term, and REALM is present */ - - /* NONCE must be present. */ - if (anonce == NULL) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, - NULL, &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_NONCE); - } - - /* Verify REALM matches */ - if (pj_stricmp(&arealm->value, &realm)) { - /* REALM doesn't match */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, - NULL, &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); - } - - /* Valid case, will validate the message integrity later */ - - } else if (realm.slen == 0 && arealm != NULL) { - /* We want to use short term credential, but client uses long - * term credential. The draft doesn't mention anything about - * switching between long term and short term. - */ - - /* For now just accept the credential, anyway it will probably - * cause wrong message integrity value later. - */ - } else if (realm.slen==0 && arealm == NULL) { - /* Short term authentication is wanted, and one is supplied */ - - /* Application MAY request NONCE to be supplied */ - if (nonce.slen != 0) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, - NULL, &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_NONCE); - } - } - - /* If NONCE is present, validate it */ - if (anonce) { - pj_bool_t ok; - - if (pol->type == PJ_STUN_POLICY_DYNAMIC) { - ok = pol->data.dynamic.verify_nonce(pol->user_data, - (arealm?&arealm->value:NULL), - &auser->value, - &anonce->value); - } else { - if (nonce.slen) { - ok = !pj_strcmp(&anonce->value, &nonce); - } else { - ok = PJ_TRUE; - } - } - - if (!ok) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_STALE_NONCE, - NULL, &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_STALE_NONCE); - } - } - - /* Get the position of MESSAGE-INTEGRITY in the packet */ - amsgi_pos = 20+msg->hdr.length-22; - if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { - /* Found MESSAGE-INTEGRITY as the last attribute */ - } else { - amsgi_pos = 0; - } - - if (amsgi_pos==0) { - amsgi_pos = 20+msg->hdr.length-8-22; - if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { - /* Found MESSAGE-INTEGRITY before FINGERPRINT */ - } else { - amsgi_pos = 0; - } - } - - if (amsgi_pos==0) { - pj_assert(!"Unable to find MESSAGE-INTEGRITY in the message!"); - return PJ_EBUG; - } - - /* Determine which key to use */ - if (realm.slen) { - calc_md5_key(md5_digest, &realm, &auser->value, &password); - key.ptr = (char*)md5_digest; - key.slen = 16; - } else { - key = password; - } - - /* Now calculate HMAC of the message */ - pj_hmac_sha1(pkt, amsgi_pos, (pj_uint8_t*)key.ptr, key.slen, digest); - - /* Compare HMACs */ - if (pj_memcmp(amsgi->hmac, digest, 20)) { - /* HMAC value mismatch */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE, - NULL, &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE); - } - - /* Everything looks okay! */ - return PJ_SUCCESS; -} - - diff --git a/pjlib-util/src/pjlib-util/stun_msg_dump.c b/pjlib-util/src/pjlib-util/stun_msg_dump.c index 21d569da..1c9ccc2e 100644 --- a/pjlib-util/src/pjlib-util/stun_msg_dump.c +++ b/pjlib-util/src/pjlib-util/stun_msg_dump.c @@ -53,9 +53,9 @@ static int print_attr(char *buffer, unsigned length, case PJ_STUN_ATTR_XOR_INTERNAL_ADDR: case PJ_STUN_ATTR_ALTERNATE_SERVER: { - const pj_stun_generic_ip_addr_attr *attr; + const pj_stun_ip_addr_attr *attr; - attr = (const pj_stun_generic_ip_addr_attr*)ahdr; + attr = (const pj_stun_ip_addr_attr*)ahdr; if (attr->addr.addr.sa_family == PJ_AF_INET) { len = pj_ansi_snprintf(p, end-p, @@ -84,9 +84,9 @@ static int print_attr(char *buffer, unsigned length, case PJ_STUN_ATTR_FINGERPRINT: case PJ_STUN_ATTR_REFRESH_INTERVAL: { - const pj_stun_generic_uint_attr *attr; + const pj_stun_uint_attr *attr; - attr = (const pj_stun_generic_uint_attr*)ahdr; + attr = (const pj_stun_uint_attr*)ahdr; len = pj_ansi_snprintf(p, end-p, ", value=%d (0x%x)\n", (pj_uint32_t)attr->value, @@ -100,9 +100,9 @@ static int print_attr(char *buffer, unsigned length, case PJ_STUN_ATTR_NONCE: case PJ_STUN_ATTR_SERVER: { - const pj_stun_generic_string_attr *attr; + const pj_stun_string_attr *attr; - attr = (pj_stun_generic_string_attr*)ahdr; + attr = (pj_stun_string_attr*)ahdr; len = pj_ansi_snprintf(p, end-p, ", value=\"%.*s\"\n", (int)attr->value.slen, @@ -112,9 +112,9 @@ static int print_attr(char *buffer, unsigned length, case PJ_STUN_ATTR_ERROR_CODE: { - const pj_stun_error_code_attr *attr; + const pj_stun_errcode_attr *attr; - attr = (const pj_stun_error_code_attr*) ahdr; + attr = (const pj_stun_errcode_attr*) ahdr; len = pj_ansi_snprintf(p, end-p, ", err_code=%d, reason=\"%.*s\"\n", attr->err_class*100 + attr->number, diff --git a/pjlib-util/src/pjlib-util/stun_server.c b/pjlib-util/src/pjlib-util/stun_server.c deleted file mode 100644 index d4d7fa8f..00000000 --- a/pjlib-util/src/pjlib-util/stun_server.c +++ /dev/null @@ -1,129 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2005 Benny Prijono <benny@prijono.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include <pjlib-util/stun_server.h> -#include <pjlib-util/errno.h> -#include <pj/assert.h> -#include <pj/pool.h> -#include <pj/string.h> - - -struct pj_stun_service -{ - pj_str_t name; - unsigned options; - void *user_data; - unsigned cb_cnt; - pj_stun_service_handler *cb; -}; - - -/* - * Create STUN service. - */ -PJ_DEF(pj_status_t) pj_stun_service_create( pj_pool_t *pool, - const char *name, - unsigned options, - unsigned handler_cnt, - pj_stun_service_handler cb[], - void *user_data, - pj_stun_service **p_svc) -{ - pj_stun_service *svc; - - PJ_ASSERT_RETURN(pool && handler_cnt && cb && p_svc, PJ_EINVAL); - - svc = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_service); - svc->options = options; - svc->user_data = user_data; - - if (!name) name = "pj_stun_service"; - - pj_strdup2_with_null(pool, &svc->name, name); - - svc->cb_cnt = handler_cnt; - svc->cb = pj_pool_calloc(pool, handler_cnt, - sizeof(pj_stun_service_handler)); - pj_memcpy(svc->cb, cb, handler_cnt * sizeof(pj_stun_service_handler)); - - *p_svc = svc; - - return PJ_SUCCESS; -} - - -/* - * Destroy STUN service - */ -PJ_DEF(pj_status_t) pj_stun_service_destroy(pj_stun_service *svc) -{ - PJ_ASSERT_RETURN(svc, PJ_EINVAL); - return PJ_SUCCESS; -} - - -/* - * Get user data associated with the STUN service. - */ -PJ_DEF(void*) pj_stun_service_get_user_data(pj_stun_service *svc) -{ - PJ_ASSERT_RETURN(svc, NULL); - return svc->user_data; -} - - -/* - * Find handler. - */ -static pj_stun_service_handler *find_handler(pj_stun_service *svc, - int msg_type) -{ - unsigned i; - - for (i=0; i<svc->cb_cnt; ++i) { - if (svc->cb[i].msg_type == msg_type) - return &svc->cb[i]; - } - - return NULL; -} - - -/* - * Instruct the STUN service to handle incoming STUN message. - */ -PJ_DEF(pj_status_t) pj_stun_service_handle_msg( pj_stun_service *svc, - void *handle_data, - const pj_stun_msg *msg) -{ - pj_stun_service_handler *handler; - - PJ_ASSERT_RETURN(svc && msg, PJ_EINVAL); - - handler = find_handler(svc, msg->hdr.type); - if (handler == NULL) - return PJLIB_UTIL_ESTUNNOHANDLER; - - return (*handler->handle_msg)(svc, handle_data, msg); -} - - -////////////////////////////////////////////////////////////////////////////// - - - diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c index 11146047..7ec9d333 100644 --- a/pjlib-util/src/pjlib-util/stun_session.c +++ b/pjlib-util/src/pjlib-util/stun_session.c @@ -27,14 +27,9 @@ struct pj_stun_session pj_stun_session_cb cb; void *user_data; - /* Long term credential */ - pj_str_t l_realm; - pj_str_t l_username; - pj_str_t l_password; - - /* Short term credential */ - pj_str_t s_username; - pj_str_t s_password; + pj_bool_t use_fingerprint; + pj_stun_auth_cred *cred; + pj_str_t srv_name; pj_stun_tx_data pending_request_list; pj_stun_tx_data cached_response_list; @@ -213,88 +208,65 @@ static void on_cache_timeout(pj_timer_heap_t *timer_heap, pj_stun_msg_destroy_tdata(tdata->sess, tdata); } +static pj_str_t *get_passwd(pj_stun_session *sess) +{ + if (sess->cred == NULL) + return NULL; + else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC) + return &sess->cred->data.static_cred.data; + else + return NULL; +} + static pj_status_t apply_msg_options(pj_stun_session *sess, pj_pool_t *pool, - unsigned options, - pj_stun_msg *msg, - pj_str_t **p_passwd) + pj_stun_msg *msg) { - pj_status_t status; + pj_status_t status = 0; + + /* The server SHOULD include a SERVER attribute in all responses */ + if (PJ_STUN_IS_RESPONSE(msg->hdr.type) || + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { + pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SERVER, + &sess->srv_name); + } /* From draft-ietf-behave-rfc3489bis-05.txt * Section 8.3.1. Formulating the Request Message + * + * Note: only put MESSAGE-INTEGRITY in non error response. */ - if (options & PJ_STUN_USE_LONG_TERM_CRED) { - pj_stun_generic_string_attr *auname; - pj_stun_msg_integrity_attr *amsgi; - pj_stun_generic_string_attr *arealm; - - *p_passwd = &sess->l_password; + if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_STATIC && + !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { + const pj_str_t *username; /* Create and add USERNAME attribute */ - status = pj_stun_generic_string_attr_create(pool, - PJ_STUN_ATTR_USERNAME, - &sess->l_username, - &auname); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &auname->hdr); + username = &sess->cred->data.static_cred.username; + status = pj_stun_msg_add_string_attr(pool, msg, + PJ_STUN_ATTR_USERNAME, + username); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); /* Add REALM only when long term credential is used */ - status = pj_stun_generic_string_attr_create(pool, - PJ_STUN_ATTR_REALM, - &sess->l_realm, - &arealm); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &arealm->hdr); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - /* Add MESSAGE-INTEGRITY attribute */ - status = pj_stun_msg_integrity_attr_create(pool, &amsgi); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &amsgi->hdr); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - } else if (options & PJ_STUN_USE_SHORT_TERM_CRED) { - pj_stun_generic_string_attr *auname; - pj_stun_msg_integrity_attr *amsgi; - - *p_passwd = &sess->s_password; - - /* Create and add USERNAME attribute */ - status = pj_stun_generic_string_attr_create(pool, - PJ_STUN_ATTR_USERNAME, - &sess->s_username, - &auname); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &auname->hdr); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + if (sess->cred->data.static_cred.realm.slen) { + const pj_str_t *realm = &sess->cred->data.static_cred.realm; + status = pj_stun_msg_add_string_attr(pool, msg, + PJ_STUN_ATTR_REALM, + realm); + } /* Add MESSAGE-INTEGRITY attribute */ - status = pj_stun_msg_integrity_attr_create(pool, &amsgi); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &amsgi->hdr); + status = pj_stun_msg_add_msgint_attr(pool, msg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - } else { - *p_passwd = NULL; - } + } /* Add FINGERPRINT attribute if necessary */ - if (options & PJ_STUN_USE_FINGERPRINT) { - pj_stun_fingerprint_attr *af; - - status = pj_stun_generic_uint_attr_create(pool, - PJ_STUN_ATTR_FINGERPRINT, - 0, &af); - PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); - - status = pj_stun_msg_add_attr(msg, &af->hdr); + if (sess->use_fingerprint) { + status = pj_stun_msg_add_uint_attr(pool, msg, + PJ_STUN_ATTR_FINGERPRINT, 0); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } @@ -333,6 +305,7 @@ static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx, PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt, const char *name, const pj_stun_session_cb *cb, + pj_bool_t fingerprint, pj_stun_session **p_sess) { pj_pool_t *pool; @@ -351,6 +324,11 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt, sess->endpt = endpt; sess->pool = pool; pj_memcpy(&sess->cb, cb, sizeof(*cb)); + sess->use_fingerprint = fingerprint; + + sess->srv_name.ptr = pj_pool_alloc(pool, 32); + sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32, + "pj_stun-%s", PJ_VERSION); pj_list_init(&sess->pending_request_list); pj_list_init(&sess->cached_response_list); @@ -393,41 +371,25 @@ PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess) return sess->user_data; } -PJ_DEF(pj_status_t) -pj_stun_session_set_long_term_credential(pj_stun_session *sess, - const pj_str_t *realm, - const pj_str_t *user, - const pj_str_t *passwd) +PJ_DEF(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess, + const pj_str_t *srv_name) { - pj_str_t nil = { NULL, 0 }; - - PJ_ASSERT_RETURN(sess, PJ_EINVAL); - - pj_mutex_lock(sess->mutex); - pj_strdup_with_null(sess->pool, &sess->l_realm, realm ? realm : &nil); - pj_strdup_with_null(sess->pool, &sess->l_username, user ? user : &nil); - pj_strdup_with_null(sess->pool, &sess->l_password, passwd ? passwd : &nil); - pj_mutex_unlock(sess->mutex); - + PJ_ASSERT_RETURN(sess && srv_name, PJ_EINVAL); + pj_strdup(sess->pool, &sess->srv_name, srv_name); return PJ_SUCCESS; } - -PJ_DEF(pj_status_t) -pj_stun_session_set_short_term_credential(pj_stun_session *sess, - const pj_str_t *user, - const pj_str_t *passwd) +PJ_DEF(void) pj_stun_session_set_credential(pj_stun_session *sess, + const pj_stun_auth_cred *cred) { - pj_str_t nil = { NULL, 0 }; - - PJ_ASSERT_RETURN(sess, PJ_EINVAL); - - pj_mutex_lock(sess->mutex); - pj_strdup_with_null(sess->pool, &sess->s_username, user ? user : &nil); - pj_strdup_with_null(sess->pool, &sess->s_password, passwd ? passwd : &nil); - pj_mutex_unlock(sess->mutex); - - return PJ_SUCCESS; + PJ_ASSERT_ON_FAIL(sess, return); + if (cred) { + if (!sess->cred) + sess->cred = pj_pool_alloc(sess->pool, sizeof(pj_stun_auth_cred)); + pj_stun_auth_cred_dup(sess->pool, sess->cred, cred); + } else { + sess->cred = NULL; + } } @@ -569,18 +531,15 @@ static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, - unsigned options, + pj_bool_t cache_res, const pj_sockaddr_t *server, unsigned addr_len, pj_stun_tx_data *tdata) { - pj_str_t *password; pj_status_t status; PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL); - tdata->options = options; - /* Allocate packet */ tdata->max_len = PJ_STUN_MAX_PKT_LEN; tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len); @@ -589,8 +548,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, pj_mutex_lock(sess->mutex); /* Apply options */ - status = apply_msg_options(sess, tdata->pool, options, - tdata->msg, &password); + status = apply_msg_options(sess, tdata->pool, tdata->msg); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); pj_mutex_unlock(sess->mutex); @@ -600,7 +558,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, /* Encode message */ status = pj_stun_msg_encode(tdata->msg, tdata->pkt, tdata->max_len, - 0, password, &tdata->pkt_size); + 0, get_passwd(sess), &tdata->pkt_size); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); pj_mutex_unlock(sess->mutex); @@ -640,7 +598,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tsx_add(sess, tdata); } else { - if ((options & PJ_STUN_CACHE_RESPONSE) && + if (cache_res && (PJ_STUN_IS_RESPONSE(tdata->msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) { @@ -687,6 +645,68 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } +/* Send response */ +static pj_status_t send_response(pj_stun_session *sess, + pj_pool_t *pool, pj_stun_msg *response, + pj_bool_t retransmission, + const pj_sockaddr_t *addr, unsigned addr_len) +{ + pj_uint8_t *out_pkt; + unsigned out_max_len, out_len; + pj_status_t status; + + /* Alloc packet buffer */ + out_max_len = PJ_STUN_MAX_PKT_LEN; + out_pkt = pj_pool_alloc(pool, out_max_len); + + /* Apply options */ + if (!retransmission) { + apply_msg_options(sess, pool, response); + } + + /* Encode */ + status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, + get_passwd(sess), &out_len); + if (status != PJ_SUCCESS) { + LOG_ERR_(sess, "Error encoding message", status); + return status; + } + + /* Print log */ + dump_tx_msg(sess, response, out_len, addr); + + /* Send packet */ + status = sess->cb.on_send_msg(sess, out_pkt, out_len, addr, addr_len); + + return status; +} + +/* Authenticate incoming message */ +static pj_status_t authenticate_msg(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + pj_pool_t *tmp_pool, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_stun_msg *response; + pj_status_t status; + + if (PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type) || sess->cred == NULL) + return PJ_SUCCESS; + + status = pj_stun_verify_credential(pkt, pkt_len, msg, sess->cred, + tmp_pool, &response); + if (status != PJ_SUCCESS && response != NULL) { + send_response(sess, tmp_pool, response, PJ_FALSE, + src_addr, src_addr_len); + } + + return status; +} + + /* Handle incoming response */ static pj_status_t on_incoming_response(pj_stun_session *sess, pj_stun_msg *msg) @@ -723,51 +743,14 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, } -/* Send response */ -static pj_status_t send_response(pj_stun_session *sess, unsigned options, - pj_pool_t *pool, pj_stun_msg *response, - const pj_sockaddr_t *addr, unsigned addr_len) -{ - pj_uint8_t *out_pkt; - unsigned out_max_len, out_len; - pj_str_t *passwd; - pj_status_t status; - - /* Alloc packet buffer */ - out_max_len = PJ_STUN_MAX_PKT_LEN; - out_pkt = pj_pool_alloc(pool, out_max_len); - - /* Apply options */ - apply_msg_options(sess, pool, options, response, &passwd); - - /* Encode */ - status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, - passwd, &out_len); - if (status != PJ_SUCCESS) { - LOG_ERR_(sess, "Error encoding message", status); - return status; - } - - /* Print log */ - dump_tx_msg(sess, response, out_len, addr); - - /* Send packet */ - status = sess->cb.on_send_msg(sess, out_pkt, out_len, addr, addr_len); - - return status; -} - -/* Handle incoming request */ -static pj_status_t on_incoming_request(pj_stun_session *sess, - pj_pool_t *tmp_pool, - const pj_uint8_t *in_pkt, - unsigned in_pkt_len, - const pj_stun_msg *msg, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) +/* For requests, check if we cache the response */ +static pj_status_t check_cached_response(pj_stun_session *sess, + pj_pool_t *tmp_pool, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) { pj_stun_tx_data *t; - pj_status_t status; /* First lookup response in response cache */ t = sess->cached_response_list.next; @@ -783,30 +766,42 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, if (t != &sess->cached_response_list) { /* Found response in the cache */ - unsigned options; PJ_LOG(5,(SNAME(sess), "Request retransmission, sending cached response")); - options = t->options; - options &= ~PJ_STUN_CACHE_RESPONSE; - pj_stun_session_send_msg(sess, options, src_addr, src_addr_len, t); + send_response(sess, tmp_pool, t->msg, PJ_TRUE, + src_addr, src_addr_len); return PJ_SUCCESS; } + return PJ_ENOTFOUND; +} + +/* Handle incoming request */ +static pj_status_t on_incoming_request(pj_stun_session *sess, + pj_pool_t *tmp_pool, + const pj_uint8_t *in_pkt, + unsigned in_pkt_len, + const pj_stun_msg *msg, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + pj_status_t status; + /* Distribute to handler, or respond with Bad Request */ if (sess->cb.on_rx_request) { status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, msg, src_addr, src_addr_len); } else { - pj_stun_msg *response = NULL; + pj_stun_msg *response; status = pj_stun_msg_create_response(tmp_pool, msg, PJ_STUN_STATUS_BAD_REQUEST, NULL, &response); if (status == PJ_SUCCESS && response) { - status = send_response(sess, 0, tmp_pool, response, - src_addr, src_addr_len); + status = send_response(sess, tmp_pool, response, + PJ_FALSE, src_addr, src_addr_len); } } @@ -861,8 +856,8 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); if (response) { - send_response(sess, 0, tmp_pool, response, - src_addr, src_addr_len); + send_response(sess, tmp_pool, response, + PJ_FALSE, src_addr, src_addr_len); } pj_pool_release(tmp_pool); return status; @@ -879,6 +874,20 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, pj_mutex_lock(sess->mutex); + /* For requests, check if we have cached response */ + status = check_cached_response(sess, tmp_pool, msg, + src_addr, src_addr_len); + if (status == PJ_SUCCESS) { + goto on_return; + } + + /* Authenticate the message */ + status = authenticate_msg(sess, packet, pkt_size, msg, tmp_pool, + src_addr, src_addr_len); + if (status != PJ_SUCCESS) + goto on_return; + + /* Handle message */ if (PJ_STUN_IS_RESPONSE(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { @@ -899,6 +908,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, status = PJ_EBUG; } +on_return: pj_mutex_unlock(sess->mutex); pj_pool_release(tmp_pool); diff --git a/pjlib-util/src/pjlib-util/stun_transaction.c b/pjlib-util/src/pjlib-util/stun_transaction.c index 02f33177..636d56d1 100644 --- a/pjlib-util/src/pjlib-util/stun_transaction.c +++ b/pjlib-util/src/pjlib-util/stun_transaction.c @@ -259,7 +259,7 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, const pj_stun_msg *msg) { - pj_stun_error_code_attr *err_attr; + pj_stun_errcode_attr *err_attr; pj_status_t status; /* Must be STUN response message */ @@ -281,7 +281,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, } /* Find STUN error code attribute */ - err_attr = (pj_stun_error_code_attr*) + err_attr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0); if (err_attr && err_attr->err_class <= 2) { @@ -300,8 +300,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, if (err_attr == NULL) { status = PJ_SUCCESS; } else { - status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_class * 100 + - err_attr->number); + status = PJLIB_UTIL_ESTUNTSXFAILED; } /* Call callback */ diff --git a/pjlib-util/src/pjstun-client/client_main.c b/pjlib-util/src/pjstun-client/client_main.c index 4f82f743..fd1b9e39 100644 --- a/pjlib-util/src/pjstun-client/client_main.c +++ b/pjlib-util/src/pjstun-client/client_main.c @@ -29,7 +29,6 @@ static struct global pj_caching_pool cp; pj_timer_heap_t *th; pj_stun_session *sess; - unsigned sess_options; pj_sock_t sock; pj_thread_t *thread; pj_bool_t quit; @@ -45,6 +44,7 @@ static struct options char *realm; char *user_name; char *password; + char *nonce; pj_bool_t use_fingerprint; } o; @@ -180,40 +180,28 @@ static int init() stun_cb.on_send_msg = &on_send_msg; stun_cb.on_request_complete = &on_request_complete; - status = pj_stun_session_create(g.endpt, NULL, &stun_cb, &g.sess); + status = pj_stun_session_create(g.endpt, NULL, &stun_cb, + o.use_fingerprint!=0, &g.sess); pj_assert(status == PJ_SUCCESS); - if (o.realm) { - pj_str_t r, u, p; + if (o.user_name) { + pj_stun_auth_cred cred; - if (o.user_name == NULL) { - printf("error: username must be specified\n"); - return PJ_EINVAL; - } - if (o.password == NULL) - o.password = ""; - g.sess_options = PJ_STUN_USE_LONG_TERM_CRED; - pj_stun_session_set_long_term_credential(g.sess, pj_cstr(&r, o.realm), - pj_cstr(&u, o.user_name), - pj_cstr(&p, o.password)); - puts("Using long term credential"); - } else if (o.user_name) { - pj_str_t u, p; - - if (o.password == NULL) - o.password = ""; - g.sess_options = PJ_STUN_USE_SHORT_TERM_CRED; - pj_stun_session_set_short_term_credential(g.sess, - pj_cstr(&u, o.user_name), - pj_cstr(&p, o.password)); - puts("Using short term credential"); + pj_bzero(&cred, sizeof(cred)); + + cred.type = PJ_STUN_AUTH_CRED_STATIC; + cred.data.static_cred.realm = pj_str(o.realm); + cred.data.static_cred.username = pj_str(o.user_name); + cred.data.static_cred.data_type = 0; + cred.data.static_cred.data = pj_str(o.password); + cred.data.static_cred.nonce = pj_str(o.nonce); + + pj_stun_session_set_credential(g.sess, &cred); + puts("Session credential set"); } else { puts("Credential not set"); } - if (o.use_fingerprint) - g.sess_options |= PJ_STUN_USE_FINGERPRINT; - status = pj_thread_create(g.pool, "stun", &worker_thread, NULL, 0, 0, &g.thread); if (status != PJ_SUCCESS) @@ -275,7 +263,7 @@ static void console_main(void) rc = pj_stun_session_create_bind_req(g.sess, &tdata); pj_assert(rc == PJ_SUCCESS); - rc = pj_stun_session_send_msg(g.sess, g.sess_options, + rc = pj_stun_session_send_msg(g.sess, PJ_FALSE, &g.dst_addr, sizeof(g.dst_addr), tdata); if (rc != PJ_SUCCESS) @@ -302,6 +290,7 @@ static void usage(void) puts(" --realm, -r Set realm of the credential"); puts(" --username, -u Set username of the credential"); puts(" --password, -p Set password of the credential"); + puts(" --nonce, -N Set NONCE"); puts(" --fingerprint, -F Use fingerprint for outgoing requests"); puts(" --help, -h"); } @@ -312,6 +301,7 @@ int main(int argc, char *argv[]) { "realm", 1, 0, 'r'}, { "username", 1, 0, 'u'}, { "password", 1, 0, 'p'}, + { "nonce", 1, 0, 'N'}, { "fingerprint",0, 0, 'F'}, { "help", 0, 0, 'h'} }; @@ -330,6 +320,9 @@ int main(int argc, char *argv[]) case 'p': o.password = pj_optarg; break; + case 'N': + o.nonce = pj_optarg; + break; case 'h': usage(); return 0; diff --git a/pjlib-util/src/pjstun-srv-test/server_main.c b/pjlib-util/src/pjstun-srv-test/server_main.c index 4825cf8e..e4fc6d5b 100644 --- a/pjlib-util/src/pjstun-srv-test/server_main.c +++ b/pjlib-util/src/pjstun-srv-test/server_main.c @@ -61,8 +61,17 @@ static struct stun_server unsigned thread_cnt; pj_thread_t *threads[16]; + } server; +static struct options +{ + char *realm; + char *user_name; + char *password; + char *nonce; + pj_bool_t use_fingerprint; +} o; static pj_status_t server_perror(const char *sender, const char *title, pj_status_t status) @@ -131,10 +140,10 @@ static pj_status_t on_rx_binding_request(pj_stun_session *sess, return status; /* Create MAPPED-ADDRESS attribute */ - status = pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg, - PJ_STUN_ATTR_MAPPED_ADDR, - PJ_FALSE, - src_addr, src_addr_len); + status = pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_MAPPED_ADDR, + PJ_FALSE, + src_addr, src_addr_len); if (status != PJ_SUCCESS) { server_perror(THIS_FILE, "Error creating response", status); pj_stun_msg_destroy_tdata(sess, tdata); @@ -144,10 +153,10 @@ static pj_status_t on_rx_binding_request(pj_stun_session *sess, /* On the presence of magic, create XOR-MAPPED-ADDRESS attribute */ if (msg->hdr.magic == PJ_STUN_MAGIC) { status = - pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg, - PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, - PJ_TRUE, - src_addr, src_addr_len); + pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, + PJ_TRUE, + src_addr, src_addr_len); if (status != PJ_SUCCESS) { server_perror(THIS_FILE, "Error creating response", status); pj_stun_msg_destroy_tdata(sess, tdata); @@ -156,7 +165,7 @@ static pj_status_t on_rx_binding_request(pj_stun_session *sess, } /* Send */ - status = pj_stun_session_send_msg(sess, PJ_STUN_CACHE_RESPONSE, + status = pj_stun_session_send_msg(sess, PJ_TRUE, src_addr, src_addr_len, tdata); return status; } @@ -263,12 +272,32 @@ static pj_status_t init_service(struct service *svc) sess_cb.on_send_msg = &on_send_msg; sess_cb.on_rx_request = &on_rx_request; status = pj_stun_session_create(server.endpt, "session", - &sess_cb, &svc->sess); + &sess_cb, + o.use_fingerprint!=0, + &svc->sess); if (status != PJ_SUCCESS) goto on_error; pj_stun_session_set_user_data(svc->sess, (void*)svc); + if (o.user_name) { + pj_stun_auth_cred cred; + + pj_bzero(&cred, sizeof(cred)); + + cred.type = PJ_STUN_AUTH_CRED_STATIC; + cred.data.static_cred.realm = pj_str(o.realm); + cred.data.static_cred.username = pj_str(o.user_name); + cred.data.static_cred.data_type = 0; + cred.data.static_cred.data = pj_str(o.password); + cred.data.static_cred.nonce = pj_str(o.nonce); + + pj_stun_session_set_credential(svc->sess, &cred); + puts("Session credential set"); + } else { + puts("Credential not set"); + } + pj_bzero(&service_callback, sizeof(service_callback)); service_callback.on_read_complete = &on_read_complete; @@ -437,8 +466,65 @@ pj_status_t server_destroy(void) } -int main() +static void usage(void) +{ + puts("Usage: pjstun_srv_test [OPTIONS]"); + puts(""); + puts("where OPTIONS:"); + puts(" --realm, -r Set realm of the credential"); + puts(" --username, -u Set username of the credential"); + puts(" --password, -p Set password of the credential"); + puts(" --nonce, -N Set NONCE"); + puts(" --fingerprint, -F Use fingerprint for outgoing requests"); + puts(" --help, -h"); +} + + +int main(int argc, char *argv[]) { + struct pj_getopt_option long_options[] = { + { "realm", 1, 0, 'r'}, + { "username", 1, 0, 'u'}, + { "password", 1, 0, 'p'}, + { "nonce", 1, 0, 'N'}, + { "fingerprint",0, 0, 'F'}, + { "help", 0, 0, 'h'} + }; + int c, opt_id; + + while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) { + switch (c) { + case 'r': + o.realm = pj_optarg; + break; + case 'u': + o.user_name = pj_optarg; + break; + case 'p': + o.password = pj_optarg; + break; + case 'N': + o.nonce = pj_optarg; + break; + case 'h': + usage(); + return 0; + case 'F': + o.use_fingerprint = PJ_TRUE; + break; + default: + printf("Argument \"%s\" is not valid. Use -h to see help", + argv[pj_optind]); + return 1; + } + } + + if (pj_optind != argc) { + puts("Error: invalid arguments"); + return 1; + } + + if (server_init()) { server_destroy(); return 1; |