summaryrefslogtreecommitdiff
path: root/pjlib-util
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-03-01 23:39:08 +0000
committerBenny Prijono <bennylp@teluu.com>2007-03-01 23:39:08 +0000
commit68446a09672bbb32172f7bebb0efb3a838a2d827 (patch)
tree5f054cbde753efdccb08f6ff020c8a1c991de486 /pjlib-util
parent33a38e34d35d880a552a250927dc7430e31f8816 (diff)
More STUN work
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1030 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib-util')
-rw-r--r--pjlib-util/include/pjlib-util/errno.h16
-rw-r--r--pjlib-util/src/pjlib-util/stun_msg.c31
-rw-r--r--pjlib-util/src/pjlib-util/stun_session.c2
-rw-r--r--pjlib-util/src/pjstun-client/client_main.c351
-rw-r--r--pjlib-util/src/pjstun-srv-test/server_main.c29
5 files changed, 329 insertions, 100 deletions
diff --git a/pjlib-util/include/pjlib-util/errno.h b/pjlib-util/include/pjlib-util/errno.h
index 3f004ca3..d33ed1a2 100644
--- a/pjlib-util/include/pjlib-util/errno.h
+++ b/pjlib-util/include/pjlib-util/errno.h
@@ -301,18 +301,24 @@
#define PJLIB_UTIL_ESTUNNOHANDLER (PJLIB_UTIL_ERRNO_START+116)/* 320116 */
/**
* @hideinitializer
- * Invalid STUN MESSAGE-INTEGRITY attribute position in message.
- * STUN MESSAGE-INTEGRITY must be put last in the message, or before
- * FINGERPRINT attribute.
+ * Found non-FINGERPRINT attribute after MESSAGE-INTEGRITY. This is not
+ * valid since MESSAGE-INTEGRITY MUST be the last attribute or the
+ * attribute right before FINGERPRINT before the message.
*/
-#define PJLIB_UTIL_ESTUNMSGINT (PJLIB_UTIL_ERRNO_START+117)/* 320117 */
+#define PJLIB_UTIL_ESTUNMSGINTPOS (PJLIB_UTIL_ERRNO_START+118)/* 320118 */
+/**
+ * @hideinitializer
+ * Found attribute after FINGERPRINT. This is not valid since FINGERPRINT
+ * MUST be the last attribute in the message.
+ */
+#define PJLIB_UTIL_ESTUNFINGERPOS (PJLIB_UTIL_ERRNO_START+119)/* 320119 */
/**
* @hideinitializer
* Missing STUN USERNAME attribute.
* When credential is included in the STUN message (MESSAGE-INTEGRITY is
* present), the USERNAME attribute must be present in the message.
*/
-#define PJLIB_UTIL_ESTUNNOUSERNAME (PJLIB_UTIL_ERRNO_START+118)/* 320118 */
+#define PJLIB_UTIL_ESTUNNOUSERNAME (PJLIB_UTIL_ERRNO_START+120)/* 320120 */
#define PJ_STATUS_FROM_STUN_CODE(code) (PJLIB_UTIL_ERRNO_START+code)
diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c
index e8153e10..6954434a 100644
--- a/pjlib-util/src/pjlib-util/stun_msg.c
+++ b/pjlib-util/src/pjlib-util/stun_msg.c
@@ -988,7 +988,7 @@ static pj_status_t decode_msg_integrity_attr(pj_pool_t *pool,
pj_stun_msg_integrity_attr *attr;
/* Check that struct size is valid */
- pj_assert(sizeof(pj_stun_msg_integrity_attr)==STUN_MSG_INTEGRITY_LEN);
+ pj_assert(sizeof(pj_stun_msg_integrity_attr)==ATTR_LEN);
/* Create attribute */
attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg_integrity_attr);
@@ -1856,9 +1856,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
/* Print each attribute */
for (i=0; i<msg->attr_count; ++i) {
const struct attr_desc *adesc;
- const pj_stun_attr_hdr *attr_hdr;
-
- attr_hdr = msg->attr[i];
+ const pj_stun_attr_hdr *attr_hdr = msg->attr[i];
if (attr_hdr->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
pj_assert(amsg_integrity == NULL);
@@ -1891,6 +1889,27 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
buf_size -= printed;
}
+ /* We may have stopped printing attribute because we found
+ * MESSAGE-INTEGRITY or FINGERPRINT. Scan the rest of the
+ * attributes.
+ */
+ for ( ++i; i<msg->attr_count; ++i) {
+ const pj_stun_attr_hdr *attr_hdr = msg->attr[i];
+
+ /* There mustn't any attribute after FINGERPRINT */
+ PJ_ASSERT_RETURN(afingerprint == NULL, PJLIB_UTIL_ESTUNFINGERPOS);
+
+ if (attr_hdr->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
+ /* There mustn't be MESSAGE-INTEGRITY before */
+ PJ_ASSERT_RETURN(amsg_integrity == NULL,
+ PJLIB_UTIL_ESTUNMSGINTPOS);
+ amsg_integrity = (pj_stun_msg_integrity_attr*) attr_hdr;
+
+ } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) {
+ afingerprint = (pj_stun_fingerprint_attr*) attr_hdr;
+ }
+ }
+
/* We MUST update the message length in the header NOW before
* calculating MESSAGE-INTEGRITY and FINGERPRINT.
* Note that length is not including the 20 bytes header.
@@ -1921,13 +1940,13 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
if (i < msg->attr_count-2) {
/* Should not happen for message generated by us */
pj_assert(PJ_FALSE);
- return PJLIB_UTIL_ESTUNMSGINT;
+ return PJLIB_UTIL_ESTUNMSGINTPOS;
} else if (i == msg->attr_count-2) {
if (msg->attr[i+1]->type != PJ_STUN_ATTR_FINGERPRINT) {
/* Should not happen for message generated by us */
pj_assert(PJ_FALSE);
- return PJLIB_UTIL_ESTUNMSGINT;
+ return PJLIB_UTIL_ESTUNMSGINTPOS;
} else {
afingerprint = (pj_stun_fingerprint_attr*) msg->attr[i+1];
}
diff --git a/pjlib-util/src/pjlib-util/stun_session.c b/pjlib-util/src/pjlib-util/stun_session.c
index 579f0aca..eea1c2ec 100644
--- a/pjlib-util/src/pjlib-util/stun_session.c
+++ b/pjlib-util/src/pjlib-util/stun_session.c
@@ -571,7 +571,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, NULL, &tdata->pkt_size);
+ 0, password, &tdata->pkt_size);
if (status != PJ_SUCCESS) {
pj_stun_msg_destroy_tdata(sess, tdata);
pj_mutex_unlock(sess->mutex);
diff --git a/pjlib-util/src/pjstun-client/client_main.c b/pjlib-util/src/pjstun-client/client_main.c
index 4ef95d85..4f82f743 100644
--- a/pjlib-util/src/pjstun-client/client_main.c
+++ b/pjlib-util/src/pjstun-client/client_main.c
@@ -19,11 +19,35 @@
#include <pjlib-util.h>
#include <pjlib.h>
-#include <conio.h>
-
#define THIS_FILE "client_main.c"
+static struct global
+{
+ pj_stun_endpoint *endpt;
+ pj_pool_t *pool;
+ 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;
+
+ pj_sockaddr_in dst_addr; /**< destination addr */
+
+} g;
+
+static struct options
+{
+ char *dst_addr;
+ char *dst_port;
+ char *realm;
+ char *user_name;
+ char *password;
+ pj_bool_t use_fingerprint;
+} o;
+
static my_perror(const char *title, pj_status_t status)
{
@@ -33,20 +57,17 @@ static my_perror(const char *title, pj_status_t status)
PJ_LOG(3,(THIS_FILE, "%s: %s", title, errmsg));
}
-static pj_status_t on_send_msg(pj_stun_tx_data *tdata,
+static pj_status_t on_send_msg(pj_stun_session *sess,
const void *pkt,
pj_size_t pkt_size,
- unsigned addr_len,
- const pj_sockaddr_t *dst_addr)
+ const pj_sockaddr_t *dst_addr,
+ unsigned addr_len)
{
- pj_sock_t sock;
pj_ssize_t len;
pj_status_t status;
- sock = (pj_sock_t) pj_stun_session_get_user_data(tdata->sess);
-
len = pkt_size;
- status = pj_sock_sendto(sock, pkt, &len, 0, dst_addr, addr_len);
+ status = pj_sock_sendto(g.sock, pkt, &len, 0, dst_addr, addr_len);
if (status != PJ_SUCCESS)
my_perror("Error sending packet", status);
@@ -54,105 +75,295 @@ static pj_status_t on_send_msg(pj_stun_tx_data *tdata,
return status;
}
-static void on_bind_response(pj_stun_session *sess,
- pj_status_t status,
- pj_stun_tx_data *request,
- const pj_stun_msg *response)
+static void on_request_complete(pj_stun_session *sess,
+ pj_status_t status,
+ pj_stun_tx_data *tdata,
+ const pj_stun_msg *response)
{
- my_perror("on_bind_response()", status);
+ if (status == PJ_SUCCESS) {
+ puts("Client transaction completes");
+ } else {
+ my_perror("Client transaction error", status);
+ }
+}
+
+static int worker_thread(void *unused)
+{
+ PJ_UNUSED_ARG(unused);
+
+ while (!g.quit) {
+ pj_time_val timeout = {0, 50};
+ pj_fd_set_t readset;
+ int n;
+
+ pj_timer_heap_poll(g.th, NULL);
+
+ PJ_FD_ZERO(&readset);
+ PJ_FD_SET(g.sock, &readset);
+
+ n = pj_sock_select(g.sock+1, &readset, NULL, NULL, &timeout);
+ if (n > 0) {
+ if (PJ_FD_ISSET(g.sock, &readset)) {
+ char buffer[512];
+ pj_ssize_t len;
+ pj_sockaddr_in addr;
+ int addrlen;
+ pj_status_t rc;
+
+ len = sizeof(buffer);
+ addrlen = sizeof(addr);
+ rc = pj_sock_recvfrom(g.sock, buffer, &len, 0, &addr, &addrlen);
+ if (rc == PJ_SUCCESS && len > 0) {
+ rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len,
+ PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET,
+ NULL, &addr, addrlen);
+ if (rc != PJ_SUCCESS)
+ my_perror("Error processing packet", rc);
+ }
+ }
+ } else if (n < 0)
+ pj_thread_sleep(50);
+ }
+
+ return 0;
}
-int main()
+static int init()
{
- pj_stun_endpoint *endpt = NULL;
- pj_pool_t *pool = NULL;
- pj_caching_pool cp;
- pj_timer_heap_t *th = NULL;
- pj_stun_session *sess;
- pj_sock_t sock = PJ_INVALID_SOCKET;
pj_sockaddr_in addr;
pj_stun_session_cb stun_cb;
- pj_stun_tx_data *tdata;
- pj_str_t s;
pj_status_t status;
+ g.sock = PJ_INVALID_SOCKET;
+
status = pj_init();
status = pjlib_util_init();
- pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
-
- pool = pj_pool_create(&cp.factory, NULL, 1000, 1000, NULL);
+ pj_caching_pool_init(&g.cp, &pj_pool_factory_default_policy, 0);
+
+ if (o.dst_addr) {
+ pj_str_t s;
+ pj_uint16_t port;
+
+ if (o.dst_port)
+ port = (pj_uint16_t) atoi(o.dst_port);
+ else
+ port = PJ_STUN_PORT;
+
+ status = pj_sockaddr_in_init(&g.dst_addr, pj_cstr(&s, o.dst_addr), port);
+ if (status != PJ_SUCCESS) {
+ my_perror("Invalid address", status);
+ return status;
+ }
+
+ printf("Destination address set to %s:%d\n", o.dst_addr, (int)port);
+ } else {
+ printf("Error: address must be specified\n");
+ return PJ_EINVAL;
+ }
+
+ g.pool = pj_pool_create(&g.cp.factory, NULL, 1000, 1000, NULL);
- status = pj_timer_heap_create(pool, 1000, &th);
+ status = pj_timer_heap_create(g.pool, 1000, &g.th);
pj_assert(status == PJ_SUCCESS);
- status = pj_stun_endpoint_create(&cp.factory, 0, NULL, th, &endpt);
+ status = pj_stun_endpoint_create(&g.cp.factory, 0, NULL, g.th, &g.endpt);
pj_assert(status == PJ_SUCCESS);
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &g.sock);
pj_assert(status == PJ_SUCCESS);
- status = pj_sockaddr_in_init(&addr, pj_cstr(&s, "127.0.0.1"), PJ_STUN_PORT);
+ status = pj_sockaddr_in_init(&addr, NULL, 0);
pj_assert(status == PJ_SUCCESS);
pj_memset(&stun_cb, 0, sizeof(stun_cb));
stun_cb.on_send_msg = &on_send_msg;
- stun_cb.on_bind_response = &on_bind_response;
+ stun_cb.on_request_complete = &on_request_complete;
- status = pj_stun_session_create(endpt, NULL, &stun_cb, &sess);
+ status = pj_stun_session_create(g.endpt, NULL, &stun_cb, &g.sess);
pj_assert(status == PJ_SUCCESS);
- pj_stun_session_set_user_data(sess, (void*)sock);
+ if (o.realm) {
+ pj_str_t r, u, p;
- status = pj_stun_session_create_bind_req(sess, &tdata);
- pj_assert(status == PJ_SUCCESS);
-
- status = pj_stun_session_send_msg(sess, 0, sizeof(addr), &addr, tdata);
- pj_assert(status == PJ_SUCCESS);
+ 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");
+ } else {
+ puts("Credential not set");
+ }
- while (1) {
- pj_fd_set_t rset;
- int n;
- pj_time_val timeout;
+ if (o.use_fingerprint)
+ g.sess_options |= PJ_STUN_USE_FINGERPRINT;
- if (kbhit()) {
- if (_getch()==27)
- break;
- }
+ status = pj_thread_create(g.pool, "stun", &worker_thread, NULL,
+ 0, 0, &g.thread);
+ if (status != PJ_SUCCESS)
+ return status;
- PJ_FD_ZERO(&rset);
- PJ_FD_SET(sock, &rset);
+ return PJ_SUCCESS;
+}
- timeout.sec = 0; timeout.msec = 100;
- n = pj_sock_select(FD_SETSIZE, &rset, NULL, NULL, &timeout);
+static int shutdown()
+{
+ if (g.thread) {
+ g.quit = 1;
+ pj_thread_join(g.thread);
+ pj_thread_destroy(g.thread);
+ g.thread = NULL;
+ }
+ if (g.sess)
+ pj_stun_session_destroy(g.sess);
+ if (g.endpt)
+ pj_stun_endpoint_destroy(g.endpt);
+ if (g.sock != PJ_INVALID_SOCKET)
+ pj_sock_close(g.sock);
+ if (g.th)
+ pj_timer_heap_destroy(g.th);
+ if (g.pool)
+ pj_pool_release(g.pool);
+
+ pj_pool_factory_dump(&g.cp.factory, PJ_TRUE);
+ pj_caching_pool_destroy(&g.cp);
+
+ return PJ_SUCCESS;
+}
- if (PJ_FD_ISSET(sock, &rset)) {
- char pkt[512];
- pj_ssize_t len;
+static void menu(void)
+{
+ puts("Menu:");
+ puts(" b Send Bind request");
+ puts(" q Quit");
+ puts("");
+ printf("Choice: ");
+}
- len = sizeof(pkt);
- status = pj_sock_recv(sock, pkt, &len, 0);
- if (status == PJ_SUCCESS) {
- pj_stun_session_on_rx_pkt(sess, pkt, len, NULL);
+static void console_main(void)
+{
+ while (!g.quit) {
+ char input[10];
+
+ menu();
+
+ fgets(input, sizeof(input), stdin);
+
+ switch (input[0]) {
+ case 'b':
+ {
+ pj_stun_tx_data *tdata;
+ pj_status_t rc;
+
+ 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,
+ &g.dst_addr, sizeof(g.dst_addr),
+ tdata);
+ if (rc != PJ_SUCCESS)
+ my_perror("Error sending STUN request", rc);
}
+ break;
+ case 'q':
+ g.quit = 1;
+ break;
+ default:
+ break;
}
-
- pj_timer_heap_poll(th, NULL);
}
+}
-on_return:
- if (sock != PJ_INVALID_SOCKET)
- pj_sock_close(sock);
- if (endpt)
- pj_stun_endpoint_destroy(endpt);
- if (th)
- pj_timer_heap_destroy(th);
- if (pool)
- pj_pool_release(pool);
- pj_caching_pool_destroy(&cp);
- return 0;
+static void usage(void)
+{
+ puts("Usage: pjstun_client TARGET [OPTIONS]");
+ puts("");
+ puts("where TARGET is \"host[:port]\"");
+ puts("");
+ puts("and 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(" --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'},
+ { "fingerprint",0, 0, 'F'},
+ { "help", 0, 0, 'h'}
+ };
+ int c, opt_id;
+ char *pos;
+ pj_status_t status;
+
+ 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 '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: TARGET is needed");
+ return 1;
+ }
+
+ if ((pos=pj_ansi_strchr(argv[pj_optind], ':')) != NULL) {
+ o.dst_addr = argv[pj_optind];
+ *pos = '\0';
+ o.dst_port = pos+1;
+ } else {
+ o.dst_addr = argv[pj_optind];
+ }
+
+ status = init();
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ console_main();
+
+on_return:
+ shutdown();
+ return status ? 1 : 0;
+}
diff --git a/pjlib-util/src/pjstun-srv-test/server_main.c b/pjlib-util/src/pjstun-srv-test/server_main.c
index 6c15b5ff..a731c0a2 100644
--- a/pjlib-util/src/pjstun-srv-test/server_main.c
+++ b/pjlib-util/src/pjstun-srv-test/server_main.c
@@ -123,7 +123,6 @@ static pj_status_t on_rx_binding_request(pj_stun_session *sess,
{
struct service *svc = (struct service *) pj_stun_session_get_user_data(sess);
pj_stun_tx_data *tdata;
- pj_stun_auth_policy pol;
pj_status_t status;
/* Create response */
@@ -131,20 +130,6 @@ static pj_status_t on_rx_binding_request(pj_stun_session *sess,
if (status != PJ_SUCCESS)
return status;
-#if 1
- pj_memset(&pol, 0, sizeof(pol));
- pol.type = PJ_STUN_POLICY_STATIC_LONG_TERM;
- pol.user_data = NULL;
- pol.data.static_long_term.realm = pj_str("realm");
- pol.data.static_long_term.username = pj_str("user");
- pol.data.static_long_term.password = pj_str("password");
- pol.data.static_long_term.nonce = pj_str("nonce");
- status = pj_stun_verify_credential(pkt, pkt_len, msg, &pol, tdata->pool,
- &tdata->msg);
- if (!tdata->msg)
- return status;
-#endif
-
/* Create MAPPED-ADDRESS attribute */
status = pj_stun_msg_add_generic_ip_addr_attr(tdata->pool, tdata->msg,
PJ_STUN_ATTR_MAPPED_ADDR,
@@ -387,15 +372,23 @@ pj_status_t server_main(void)
}
#else
pj_status_t status;
- char line[10];
status = pj_thread_create(server.pool, "stun_server", &worker_thread, NULL,
0, 0, &server.threads[0]);
if (status != PJ_SUCCESS)
return server_perror(THIS_FILE, "create_thread() error", status);
- puts("Press ENTER to quit");
- fgets(line, sizeof(line), stdin);
+ while (!server.thread_quit_flag) {
+ char line[10];
+
+ printf("Menu:\n"
+ " q Quit\n"
+ "Choice:");
+
+ fgets(line, sizeof(line), stdin);
+ if (line[0] == 'q')
+ server.thread_quit_flag = 1;
+ }
#endif