From 20968030aa4e9d15c000cca74aa9a3b575c7f41e Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 22 Feb 2007 02:09:23 +0000 Subject: Continuing work on the new STUN framework, partly implemented the client session git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@993 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/build/pjlib_util.dsp | 4 + pjlib-util/build/pjlib_util.dsw | 18 + pjlib-util/build/pjstun_client.dsp | 110 +++++ pjlib-util/include/pjlib-util/stun_msg.h | 24 +- pjlib-util/include/pjlib-util/stun_transaction.h | 36 +- pjlib-util/src/pjlib-util/stun_msg_dump.c | 208 ++++++++++ pjlib-util/src/pjlib-util/stun_transaction.c | 44 +- pjlib-util/src/pjstun-client/client_main.c | 25 ++ pjlib-util/src/pjstun-client/stun_session.c | 499 +++++++++++++++++++++++ pjlib-util/src/pjstun-client/stun_session.h | 114 ++++++ 10 files changed, 1062 insertions(+), 20 deletions(-) create mode 100644 pjlib-util/build/pjstun_client.dsp create mode 100644 pjlib-util/src/pjlib-util/stun_msg_dump.c create mode 100644 pjlib-util/src/pjstun-client/client_main.c create mode 100644 pjlib-util/src/pjstun-client/stun_session.c create mode 100644 pjlib-util/src/pjstun-client/stun_session.h diff --git a/pjlib-util/build/pjlib_util.dsp b/pjlib-util/build/pjlib_util.dsp index c24f6342..a4a0e09d 100644 --- a/pjlib-util/build/pjlib_util.dsp +++ b/pjlib-util/build/pjlib_util.dsp @@ -137,6 +137,10 @@ SOURCE="..\src\pjlib-util\stun_msg.c" # End Source File # Begin Source File +SOURCE="..\src\pjlib-util\stun_msg_dump.c" +# End Source File +# Begin Source File + SOURCE="..\src\pjlib-util\stun_server.c" # End Source File # Begin Source File diff --git a/pjlib-util/build/pjlib_util.dsw b/pjlib-util/build/pjlib_util.dsw index 5831c8c3..519c987a 100644 --- a/pjlib-util/build/pjlib_util.dsw +++ b/pjlib-util/build/pjlib_util.dsw @@ -57,6 +57,24 @@ Package=<4> ############################################################################### +Project: "pjstun_client"=".\pjstun_client.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pjlib + End Project Dependency + Begin Project Dependency + Project_Dep_Name pjlib_util + End Project Dependency +}}} + +############################################################################### + Project: "pjstun_srv"=".\pjstun_srv.dsp" - Package Owner=<4> Package=<5> diff --git a/pjlib-util/build/pjstun_client.dsp b/pjlib-util/build/pjstun_client.dsp new file mode 100644 index 00000000..b4cbdb3a --- /dev/null +++ b/pjlib-util/build/pjstun_client.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="pjstun_client" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=pjstun_client - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pjstun_client.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pjstun_client.mak" CFG="pjstun_client - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pjstun_client - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pjstun_client - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pjstun_client - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "./output/pjstun-client-i386-win32-vc6-release" +# PROP BASE Intermediate_Dir "./output/pjstun-client-i386-win32-vc6-release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "./output/pjstun-client-i386-win32-vc6-release" +# PROP Intermediate_Dir "./output/pjstun-client-i386-win32-vc6-release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include" /I "../../pjlib/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/pjstun-client-i386-win32-vc6-release.exe" + +!ELSEIF "$(CFG)" == "pjstun_client - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "./output/pjstun-client-i386-win32-vc6-debug" +# PROP BASE Intermediate_Dir "./output/pjstun-client-i386-win32-vc6-debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "./output/pjstun-client-i386-win32-vc6-debug" +# PROP Intermediate_Dir "./output/pjstun-client-i386-win32-vc6-debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 netapi32.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"../bin/pjstun-client-i386-win32-vc6-debug.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "pjstun_client - Win32 Release" +# Name "pjstun_client - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE="..\src\pjstun-client\client_main.c" +# End Source File +# Begin Source File + +SOURCE="..\src\pjstun-client\stun_session.c" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE="..\src\pjstun-client\stun_session.h" +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/pjlib-util/include/pjlib-util/stun_msg.h b/pjlib-util/include/pjlib-util/stun_msg.h index e8a4f1af..f5e697f0 100644 --- a/pjlib-util/include/pjlib-util/stun_msg.h +++ b/pjlib-util/include/pjlib-util/stun_msg.h @@ -1189,12 +1189,29 @@ PJ_DECL(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, unsigned *p_msg_len); +/** + * Dump STUN message to a printable string output. + * + * @param msg The STUN message + * @param buffer Buffer where the printable string output will + * be printed on. + * @param length On input, specify the maximum length of the buffer. + * On output, it will be filled up with the actual + * length of the output string. + * + * @return The message string output. + */ +PJ_DECL(char*) pj_stun_msg_dump(const pj_stun_msg *msg, + char *buffer, + unsigned *length); + + /** * Find STUN attribute in the STUN message, starting from the specified * index. * * @param msg The STUN message. - * @param attr_type The attribute type to be found. + * @param attr_type The attribute type to be found, from pj_stun_attr_type. * @param start_index The start index of the attribute in the message. * Specify zero to start searching from the first * attribute. @@ -1212,7 +1229,7 @@ PJ_DECL(pj_stun_attr_hdr*) pj_stun_msg_find_attr(const pj_stun_msg *msg, * the port and ip_addr parameters are in host byte order. * * @param pool The pool to allocate memory from. - * @param attr_type Attribute type. + * @param attr_type Attribute type, from #pj_stun_attr_type. * @param xor_ed If non-zero, the port and address will be XOR-ed * with magic, to make the XOR-MAPPED-ADDRESS attribute. * @param addr_len Length of \a addr parameter. @@ -1234,6 +1251,7 @@ pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool, * Create a STUN generic string attribute. * * @param pool The pool to allocate memory from. + * @param attr_type Attribute type, from #pj_stun_attr_type. * @param value The string value to be assigned to the attribute. * @param p_attr Pointer to receive the attribute. * @@ -1315,7 +1333,7 @@ pj_stun_unknown_attr_create(pj_pool_t *pool, * Create a blank binary attribute. * * @param pool The pool to allocate memory from. - * @param attr_type The attribute type. + * @param attr_type The attribute type, from #pj_stun_attr_type. * @param p_attr Pointer to receive the attribute. * * @return PJ_SUCCESS on success or the appropriate error code. diff --git a/pjlib-util/include/pjlib-util/stun_transaction.h b/pjlib-util/include/pjlib-util/stun_transaction.h index 2cacef36..ee57d11e 100644 --- a/pjlib-util/include/pjlib-util/stun_transaction.h +++ b/pjlib-util/include/pjlib-util/stun_transaction.h @@ -73,7 +73,7 @@ typedef struct pj_stun_tsx_cb */ void (*on_complete)(pj_stun_client_tsx *tsx, pj_status_t status, - pj_stun_msg *response); + const pj_stun_msg *response); /** * This callback is called by the STUN transaction when it wants to send @@ -123,6 +123,16 @@ PJ_DECL(pj_status_t) pj_stun_client_tsx_create( pj_stun_endpoint *endpt, PJ_DECL(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx); +/** + * Check if transaction has completed. + * + * @param tsx The STUN transaction. + * + * @return Non-zero if transaction has completed. + */ +PJ_DECL(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx); + + /** * Associate an arbitrary data with the STUN transaction. This data * can be then retrieved later from the transaction, by using @@ -186,12 +196,34 @@ PJ_DECL(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, * * @return PJ_SUCCESS on success or the appropriate error code. */ -PJ_DECL(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, +PJ_DECL(pj_status_t) pj_stun_client_tsx_on_rx_pkt(pj_stun_client_tsx *tsx, const void *packet, pj_size_t pkt_size, unsigned *parsed_len); +/** + * Notify the STUN transaction about the arrival of STUN response. + * If the STUN response contains a final error (300 and greater), the + * transaction will be terminated and callback will be called. If the + * STUN response contains response code 100-299, retransmission + * will cease, but application must still call this function again + * with a final response later to allow the transaction to complete. + * + * @param tsx The STUN client transaction instance. + * @param packet The incoming packet. + * @param pkt_size Size of the incoming packet. + * @param parsed_len Optional pointer to receive the number of bytes + * that have been parsed from the incoming packet + * for the STUN message. This is useful if the + * STUN transaction is running over stream oriented + * socket such as TCP or TLS. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, + const pj_stun_msg *msg); + /** * @} diff --git a/pjlib-util/src/pjlib-util/stun_msg_dump.c b/pjlib-util/src/pjlib-util/stun_msg_dump.c new file mode 100644 index 00000000..2def62e4 --- /dev/null +++ b/pjlib-util/src/pjlib-util/stun_msg_dump.c @@ -0,0 +1,208 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 +#include +#include +#include + + +#define APPLY() if (len < 1 || len >= (end-p)) \ + goto on_return; \ + p += len + +static int print_attr(char *buffer, unsigned length, + const pj_stun_attr_hdr *ahdr) +{ + char *p = buffer, *end = buffer + length; + int len; + + len = pj_ansi_snprintf(buffer, end-p, + " %s: length=%d", + pj_stun_get_attr_name(ahdr->type), + (int)ahdr->length); + APPLY(); + + + switch (ahdr->type) { + case PJ_STUN_ATTR_MAPPED_ADDR: + case PJ_STUN_ATTR_RESPONSE_ADDR: + case PJ_STUN_ATTR_SOURCE_ADDR: + case PJ_STUN_ATTR_CHANGED_ADDR: + case PJ_STUN_ATTR_REFLECTED_FROM: + case PJ_STUN_ATTR_REMOTE_ADDRESS: + case PJ_STUN_ATTR_RELAY_ADDRESS: + case PJ_STUN_ATTR_XOR_MAPPED_ADDRESS: + case PJ_STUN_ATTR_REQUESTED_IP: + case PJ_STUN_ATTR_XOR_REFLECTED_FROM: + case PJ_STUN_ATTR_XOR_INTERNAL_ADDR: + case PJ_STUN_ATTR_ALTERNATE_SERVER: + { + const pj_stun_generic_ip_addr_attr *attr; + + attr = (const pj_stun_generic_ip_addr_attr*)ahdr; + + if (attr->addr.addr.sa_family == PJ_AF_INET) { + len = pj_ansi_snprintf(buffer, end-p, + ", IPv4 addr=%s:%d\n", + pj_inet_ntoa(attr->addr.ipv4.sin_addr), + pj_ntohs(attr->addr.ipv4.sin_port)); + + } else if (attr->addr.addr.sa_family == PJ_AF_INET6) { + len = pj_ansi_snprintf(buffer, end-p, + ", IPv6 addr present\n"); + } else { + len = pj_ansi_snprintf(buffer, end-p, + ", INVALID ADDRESS FAMILY!\n"); + } + } + break; + + case PJ_STUN_ATTR_CHANGE_REQUEST: + case PJ_STUN_ATTR_LIFETIME: + case PJ_STUN_ATTR_BANDWIDTH: + case PJ_STUN_ATTR_REQUESTED_ADDR_TYPE: + case PJ_STUN_ATTR_REQUESTED_PORT_PROPS: + case PJ_STUN_ATTR_REQUESTED_TRANSPORT: + case PJ_STUN_ATTR_TIMER_VAL: + case PJ_STUN_ATTR_PRIORITY: + case PJ_STUN_ATTR_FINGERPRINT: + case PJ_STUN_ATTR_REFRESH_INTERVAL: + { + const pj_stun_generic_uint_attr *attr; + + attr = (const pj_stun_generic_uint_attr*)ahdr; + len = pj_ansi_snprintf(buffer, end-p, + ", value=%d (%x)\n", + (pj_uint32_t)attr->value, + (pj_uint32_t)attr->value); + } + break; + + case PJ_STUN_ATTR_USERNAME: + case PJ_STUN_ATTR_PASSWORD: + case PJ_STUN_ATTR_REALM: + case PJ_STUN_ATTR_NONCE: + case PJ_STUN_ATTR_SERVER: + { + const pj_stun_generic_string_attr *attr; + + attr = (pj_stun_generic_string_attr*)ahdr; + len = pj_ansi_snprintf(buffer, end-p, + ", value=\"%.*s\"\n", + (int)attr->value.slen, + attr->value.ptr); + } + break; + + case PJ_STUN_ATTR_ERROR_CODE: + { + const pj_stun_error_code_attr *attr; + + attr = (const pj_stun_error_code_attr*) ahdr; + len = pj_ansi_snprintf(buffer, end-p, + ", err_code=%d, reason=\"%.*s\"\n", + attr->err_class*100 + attr->number, + (int)attr->reason.slen, + attr->reason.ptr); + } + break; + + case PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES: + { + const pj_stun_unknown_attr *attr; + unsigned j; + + attr = (const pj_stun_unknown_attr*) ahdr; + + len = pj_ansi_snprintf(buffer, end-p, + ", unknown list:"); + APPLY(); + + for (j=0; jattr_count; ++j) { + len = pj_ansi_snprintf(buffer, end-p, + " %d", + (int)attr->attrs[j]); + APPLY(); + } + } + break; + + case PJ_STUN_ATTR_MESSAGE_INTEGRITY: + case PJ_STUN_ATTR_DATA: + case PJ_STUN_ATTR_USE_CANDIDATE: + default: + len = pj_ansi_snprintf(buffer, end-p, "\n"); + + break; + } + + APPLY(); + + return (p-buffer); + +on_return: + return len; +} + + +/* + * Dump STUN message to a printable string output. + */ +PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, + char *buffer, + unsigned *length) +{ + char *p, *end; + int len; + unsigned i; + + PJ_ASSERT_RETURN(msg && buffer && length, NULL); + + p = buffer; + end = buffer + (*length); + + len = pj_ansi_snprintf(p, end-p, "STUN %s %s\n", + pj_stun_get_method_name(msg->hdr.type), + pj_stun_get_class_name(msg->hdr.type)); + APPLY(); + + len = pj_ansi_snprintf(p, end-p, + " Hdr: length=%d, magic=%x, tsx_id=%x %x %x\n" + " Attributes:\n", + msg->hdr.length, + msg->hdr.magic, + *(pj_uint32_t*)&msg->hdr.tsx_id[0], + *(pj_uint32_t*)&msg->hdr.tsx_id[4], + *(pj_uint32_t*)&msg->hdr.tsx_id[8]); + APPLY(); + + for (i=0; iattr_count; ++i) { + len = print_attr(p, end-p, msg->attr[i]); + APPLY(); + } + +on_return: + *p = '\0'; + *length = (p-buffer); + return buffer; + +} + + +#undef APPLY diff --git a/pjlib-util/src/pjlib-util/stun_transaction.c b/pjlib-util/src/pjlib-util/stun_transaction.c index 6720b8d7..701122fe 100644 --- a/pjlib-util/src/pjlib-util/stun_transaction.c +++ b/pjlib-util/src/pjlib-util/stun_transaction.c @@ -252,29 +252,16 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, } + /* * Notify the STUN transaction about the arrival of STUN response. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, - const void *packet, - pj_size_t pkt_size, - unsigned *parsed_len) + const pj_stun_msg *msg) { - pj_stun_msg *msg; pj_stun_error_code_attr *err_attr; pj_status_t status; - PJ_ASSERT_RETURN(tsx && packet && pkt_size, PJ_EINVAL); - - /* Try to parse the message */ - status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet, - pkt_size, 0, &msg, parsed_len, - NULL, NULL, NULL); - if (status != PJ_SUCCESS) { - stun_perror(tsx, "STUN msg_decode() error", status); - return status; - } - /* Must be STUN response message */ if (!PJ_STUN_IS_RESPONSE(msg->hdr.type) && !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) @@ -329,5 +316,32 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, } return PJ_SUCCESS; + +} + + +/* + * Notify the STUN transaction about the arrival of STUN response. + */ +PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_pkt(pj_stun_client_tsx *tsx, + const void *packet, + pj_size_t pkt_size, + unsigned *parsed_len) +{ + pj_stun_msg *msg; + pj_status_t status; + + PJ_ASSERT_RETURN(tsx && packet && pkt_size, PJ_EINVAL); + + /* Try to parse the message */ + status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet, + pkt_size, 0, &msg, parsed_len, + NULL, NULL, NULL); + if (status != PJ_SUCCESS) { + stun_perror(tsx, "STUN msg_decode() error", status); + return status; + } + + return pj_stun_client_tsx_on_rx_msg(tsx, msg); } diff --git a/pjlib-util/src/pjstun-client/client_main.c b/pjlib-util/src/pjstun-client/client_main.c new file mode 100644 index 00000000..7dc0540d --- /dev/null +++ b/pjlib-util/src/pjstun-client/client_main.c @@ -0,0 +1,25 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 +#include + + +#define THIS_FILE "client_main.c" +#define MAX_THREADS 8 + diff --git a/pjlib-util/src/pjstun-client/stun_session.c b/pjlib-util/src/pjstun-client/stun_session.c new file mode 100644 index 00000000..571b723d --- /dev/null +++ b/pjlib-util/src/pjstun-client/stun_session.c @@ -0,0 +1,499 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 "stun_session.h" +#include + +struct pj_stun_session +{ + pj_stun_endpoint *endpt; + pj_pool_t *pool; + pj_stun_session_cb cb; + void *user_data; + + pj_str_t realm; + pj_str_t username; + pj_str_t password; + + pj_bool_t fingerprint_enabled; + + pj_stun_tx_data pending_request_list; +}; + +#define SNAME(s_) ((s_)->pool->obj_name) + +#if PJ_LOG_MAX_LEVEL >= 5 +# define TRACE_(expr) PJ_LOG(5,expr) +#else +# define TRACE_(expr) +#endif + +#if PJ_LOG_MAX_LEVEL >= 4 +# define LOG_ERR_(sess, title, rc) +static void stun_perror(pj_stun_session *sess, const char *title, + pj_status_t status) +{ + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + + PJ_LOG(4,(SNAME(sess), "%s: %s", title, errmsg)); +} + +#else +# define ERR_(sess, title, rc) +#endif + +#define TDATA_POOL_SIZE 1024 +#define TDATA_POOL_INC 1024 + + +static void tsx_on_complete(pj_stun_client_tsx *tsx, + pj_status_t status, + const pj_stun_msg *response); +static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx, + const void *stun_pkt, + pj_size_t pkt_size); + +static pj_stun_tsx_cb tsx_cb = +{ + &tsx_on_complete, + &tsx_on_send_msg +}; + + +static pj_status_t tsx_add(pj_stun_session *sess, + pj_stun_tx_data *tdata) +{ + pj_list_push_back(&sess->pending_request_list, tdata); + return PJ_SUCCESS; +} + +static pj_status_t tsx_erase(pj_stun_session *sess, + pj_stun_tx_data *tdata) +{ + pj_list_erase(tdata); + return PJ_SUCCESS; +} + +static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess, + const pj_stun_msg *msg) +{ + pj_stun_tx_data *tdata; + + tdata = sess->pending_request_list.next; + while (tdata != &sess->pending_request_list) { + pj_assert(sizeof(tdata->client_key)==sizeof(msg->hdr.tsx_id)); + if (pj_memcmp(tdata->client_key, msg->hdr.tsx_id, + sizeof(msg->hdr.tsx_id))==0) + { + return tdata; + } + tdata = tdata->next; + } + + return NULL; +} + +static pj_status_t create_tdata(pj_stun_session *sess, + unsigned msg_type, + void *user_data, + pj_stun_tx_data **p_tdata) +{ + pj_pool_t *pool; + pj_status_t status; + pj_stun_tx_data *tdata; + + /* Create pool and initialize basic tdata attributes */ + pool = pj_pool_create(sess->endpt->pf, "tdata%p", + TDATA_POOL_SIZE, TDATA_POOL_INC, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + tdata = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_tx_data); + tdata->pool = pool; + tdata->sess = sess; + tdata->user_data = tdata; + + /* Create STUN message */ + status = pj_stun_msg_create(pool, msg_type, PJ_STUN_MAGIC, + NULL, &tdata->msg); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + + /* If this is a request, then copy the request's transaction ID + * as the transaction key. + */ + if (PJ_STUN_IS_REQUEST(msg_type)) { + pj_assert(sizeof(tdata->client_key)==sizeof(tdata->msg->hdr.tsx_id)); + pj_memcpy(tdata->client_key, tdata->msg->hdr.tsx_id, + sizeof(tdata->msg->hdr.tsx_id)); + } + + *p_tdata = tdata; + + return PJ_SUCCESS; +} + +static void destroy_tdata(pj_stun_tx_data *tdata) +{ + if (tdata->client_tsx) { + tsx_erase(tdata->sess, tdata); + pj_stun_client_tsx_destroy(tdata->client_tsx); + tdata->client_tsx = NULL; + } + + pj_pool_release(tdata->pool); +} + +static pj_status_t session_apply_req(pj_stun_session *sess, + pj_pool_t *pool, + pj_stun_msg *msg) +{ + pj_status_t status; + + /* From draft-ietf-behave-rfc3489bis-05.txt + * Section 8.3.1. Formulating the Request Message + */ + if (sess->realm.slen || sess->username.slen) { + pj_stun_generic_string_attr *auname; + pj_stun_msg_integrity_attr *amsgi; + + /* Create and add USERNAME attribute */ + status = pj_stun_generic_string_attr_create(sess->pool, + PJ_STUN_ATTR_USERNAME, + &sess->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->realm.slen) { + /* Add REALM only when long term credential is used */ + pj_stun_generic_string_attr *arealm; + status = pj_stun_generic_string_attr_create(sess->pool, + PJ_STUN_ATTR_REALM, + &sess->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(sess->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); + + PJ_TODO(COMPUTE_MESSAGE_INTEGRITY); + + } + + /* Add FINGERPRINT attribute if necessary */ + if (sess->fingerprint_enabled) { + pj_stun_fingerprint_attr *af; + + status = pj_stun_generic_uint_attr_create(sess->pool, + PJ_STUN_ATTR_FINGERPRINT, + 0, &af); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + status = pj_stun_msg_add_attr(msg, &af->hdr); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + } + + return PJ_SUCCESS; +} + + + + + + +PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_endpoint *endpt, + const char *name, + const pj_stun_session_cb *cb, + pj_stun_session **p_sess) +{ + pj_pool_t *pool; + pj_stun_session *sess; + + PJ_ASSERT_RETURN(endpt && cb && p_sess, PJ_EINVAL); + + pool = pj_pool_create(endpt->pf, name, 4000, 4000, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + sess = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_session); + sess->endpt = endpt; + sess->pool = pool; + pj_memcpy(&sess->cb, cb, sizeof(*cb)); + + pj_list_init(&sess->pending_request_list); + + *p_sess = sess; + + PJ_TODO(MUTEX_PROTECTION); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) +{ + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + pj_pool_release(sess->pool); + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess, + void *user_data) +{ + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + sess->user_data = user_data; + return PJ_SUCCESS; +} + +PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess) +{ + PJ_ASSERT_RETURN(sess, NULL); + return sess->user_data; +} + +PJ_DEF(pj_status_t) pj_stun_session_set_credential( pj_stun_session *sess, + const pj_str_t *realm, + const pj_str_t *user, + const pj_str_t *passwd) +{ + pj_str_t empty = { NULL, 0 }; + + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + pj_strdup_with_null(sess->pool, &sess->realm, realm ? realm : &empty); + pj_strdup_with_null(sess->pool, &sess->username, user ? user : &empty); + pj_strdup_with_null(sess->pool, &sess->password, passwd ? passwd : &empty); + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, + pj_bool_t enabled) +{ + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + sess->fingerprint_enabled = enabled; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + pj_pool_t *pool; + pj_stun_tx_data *tdata; + pj_status_t status; + + PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); + + status = create_tdata(sess, PJ_STUN_BINDING_REQUEST, NULL, &tdata); + if (status != PJ_SUCCESS) + return status; + + status = session_apply_req(sess, pool, tdata->msg); + if (status != PJ_SUCCESS) { + destroy_tdata(tdata); + return status; + } + + *p_tdata = tdata; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + + +PJ_DEF(pj_status_t) +pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_create_connect_req( pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) +pj_stun_session_create_connection_status_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_create_send_ind( pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_create_data_ind( pj_stun_session *sess, + pj_stun_tx_data **p_tdata) +{ + PJ_ASSERT_RETURN(PJ_FALSE, PJ_ENOTSUP); +} + +PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, + unsigned addr_len, + const pj_sockaddr_t *server, + pj_stun_tx_data *tdata) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL); + + if (PJ_LOG_MAX_LEVEL >= 5) { + char buf[512]; + unsigned buflen = sizeof(buf); + const char *dst_name; + int dst_port; + const pj_sockaddr *dst = (const pj_sockaddr*)server; + + if (dst->sa_family == PJ_AF_INET) { + const pj_sockaddr_in *dst4 = (const pj_sockaddr_in*)dst; + dst_name = pj_inet_ntoa(dst4->sin_addr); + dst_port = pj_ntohs(dst4->sin_port); + } else if (dst->sa_family == PJ_AF_INET6) { + const pj_sockaddr_in6 *dst6 = (const pj_sockaddr_in6*)dst; + dst_name = "IPv6"; + dst_port = pj_ntohs(dst6->sin6_port); + } else { + LOG_ERR_(sess, "Invalid address family", PJ_EINVAL); + return PJ_EINVAL; + } + + PJ_LOG(5,(SNAME(sess), + "Sending STUN message to %s:%d:\n" + "%s\n", + dst_name, dst_port, + pj_stun_msg_dump(tdata->msg, buf, &buflen))); + } + + + /* If this is a STUN request message, then send the request with + * a new STUN client transaction. + */ + if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) { + + /* Create STUN client transaction */ + status = pj_stun_client_tsx_create(sess->endpt, &tsx_cb, + &tdata->client_tsx); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata); + + /* Send the request! */ + status = pj_stun_client_tsx_send_msg(tdata->client_tsx, PJ_TRUE, + tdata->msg); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + LOG_ERR_(sess, "Error sending STUN request", status); + return status; + } + + /* Add to pending request list */ + tsx_add(sess, tdata); + + } else { + /* Otherwise for non-request message, send directly to transport. */ + status = sess->cb.on_send_msg(tdata, addr_len, server); + } + + + return status; +} + + +PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + const void *packet, + pj_size_t pkt_size, + unsigned *parsed_len) +{ + pj_stun_msg *msg; + pj_status_t status; + + PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL); + + /* Try to parse the message */ + status = pj_stun_msg_decode(tsx->pool, (const pj_uint8_t*)packet, + pkt_size, 0, &msg, parsed_len, + NULL, NULL, NULL); + if (status != PJ_SUCCESS) { + LOG_ERR_(sess, "STUN msg_decode() error", status); + return status; + } + + if (PJ_STUN_IS_RESPONSE(msg->hdr.type) || + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { + pj_stun_tx_data *tdata; + + /* Lookup pending client transaction */ + tdata = tsx_lookup(sess, msg); + if (tdata == NULL) { + LOG_ERR_(sess, "STUN error finding transaction", PJ_ENOTFOUND); + return PJ_ENOTFOUND; + } + + /* Pass the response to the transaction. + * If the message is accepted, transaction callback will be called, + * and this will call the session callback too. + */ + status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg); + if (status != PJ_SUCCESS) + return status; + + /* If transaction has completed, destroy the transmit data. + * This will remove the transaction from the pending list too. + */ + if (pj_stun_client_tsx_is_complete(tdata->client_tsx)) { + destroy_tdata(tdata); + tdata = NULL; + } + + return PJ_SUCCESS; + + } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { + + + } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { + + + } else { + pj_assert(!"Unexpected!"); + return PJ_EBUG; + } + +} + diff --git a/pjlib-util/src/pjstun-client/stun_session.h b/pjlib-util/src/pjstun-client/stun_session.h new file mode 100644 index 00000000..f9759cb9 --- /dev/null +++ b/pjlib-util/src/pjstun-client/stun_session.h @@ -0,0 +1,114 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2005 Benny Prijono + * + * 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 + */ +#ifndef __PJLIB_UTIL_STUN_SESSION_H__ +#define __PJLIB_UTIL_STUN_SESSION_H__ + +#include +#include +#include +#include + + +typedef struct pj_stun_tx_data pj_stun_tx_data; +typedef struct pj_stun_session pj_stun_session; + +typedef struct pj_stun_session_cb +{ + pj_status_t (*on_send_msg)(pj_stun_tx_data *tdata, + unsigned addr_len, + const pj_sockaddr_t *dst_addr); + + void (*on_bind_response)(void *user_data, pj_status_t status, pj_stun_msg *response); + void (*on_allocate_response)(void *user_data, pj_status_t status, pj_stun_msg *response); + void (*on_set_active_destination_response)(void *user_data, pj_status_t status, pj_stun_msg *response); + void (*on_connect_response)(void *user_data, pj_status_t status, pj_stun_msg *response); +} pj_stun_session_cb; + + +struct pj_stun_tx_data +{ + PJ_DECL_LIST_MEMBER(struct pj_stun_tx_data); + + pj_pool_t *pool; + pj_stun_session *sess; + pj_stun_msg *msg; + void *user_data; + + pj_stun_client_tsx *client_tsx; + pj_uint8_t client_key[12]; +}; + + +PJ_DECL(pj_status_t) pj_stun_session_create(pj_stun_endpoint *endpt, + const char *name, + const pj_stun_session_cb *cb, + pj_stun_session **p_sess); + +PJ_DECL(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess); + +PJ_DECL(pj_status_t) pj_stun_session_set_user_data(pj_stun_session *sess, + void *user_data); + +PJ_DECL(void*) pj_stun_session_get_user_data(pj_stun_session *sess); + +PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, + const pj_str_t *realm, + const pj_str_t *user, + const pj_str_t *passwd); + +PJ_DECL(pj_status_t) pj_stun_session_enable_fingerprint(pj_stun_session *sess, + pj_bool_t enabled); + +PJ_DECL(pj_status_t) pj_stun_session_create_bind_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_allocate_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) +pj_stun_session_create_set_active_destination_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_connect_req(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) +pj_stun_session_create_connection_status_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_send_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_create_data_ind(pj_stun_session *sess, + pj_stun_tx_data **p_tdata); + +PJ_DECL(pj_status_t) pj_stun_session_send_msg(pj_stun_session *sess, + unsigned addr_len, + const pj_sockaddr_t *server, + pj_stun_tx_data *tdata); + +PJ_DECL(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, + const void *packet, + pj_size_t pkt_size, + unsigned *parsed_len); + + + +#endif /* __PJLIB_UTIL_STUN_SESSION_H__ */ + -- cgit v1.2.3