mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-23 20:51:58 +00:00
Merge branch 'master' of git://git.freeswitch.org/freeswitch
This commit is contained in:
@@ -1001,6 +1001,7 @@
|
||||
/unimrcp/build/acmacros/lt~obsolete.m4
|
||||
/*.tar.gz
|
||||
/celt-0.7.1/
|
||||
/celt-0.10.0/
|
||||
/flite-1.3.99/
|
||||
/freeradius-client-1.1.6/
|
||||
/iksemel/test/tst-dom
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
autoheader
|
||||
libtoolize --force --copy
|
||||
aclocal
|
||||
|
||||
@@ -4177,6 +4177,19 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_freetdm_load)
|
||||
freetdm_endpoint_interface->state_handler = &freetdm_state_handlers;
|
||||
|
||||
SWITCH_ADD_API(commands_api_interface, "ftdm", "FreeTDM commands", ft_function, FT_SYNTAX);
|
||||
switch_console_set_complete("add ftdm start");
|
||||
switch_console_set_complete("add ftdm stop");
|
||||
switch_console_set_complete("add ftdm retart");
|
||||
switch_console_set_complete("add ftdm dump");
|
||||
switch_console_set_complete("add ftdm sigstatus get");
|
||||
switch_console_set_complete("add ftdm sigstatus set");
|
||||
switch_console_set_complete("add ftdm trace");
|
||||
switch_console_set_complete("add ftdm notrace");
|
||||
switch_console_set_complete("add ftdm q931_pcap");
|
||||
switch_console_set_complete("add ftdm gains");
|
||||
switch_console_set_complete("add ftdm dtmf on");
|
||||
switch_console_set_complete("add ftdm dtmf off");
|
||||
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "disable_ec", "Disable Echo Canceller", "Disable Echo Canceller", disable_ec_function, "", SAF_NONE);
|
||||
SWITCH_ADD_APP(app_interface, "disable_dtmf", "Disable DTMF Detection", "Disable DTMF Detection", disable_dtmf_function, "", SAF_NONE);
|
||||
|
||||
+27
-13
@@ -634,6 +634,10 @@ static ftdm_status_t ftdm_span_destroy(ftdm_span_t *span)
|
||||
ftdm_mutex_lock(span->mutex);
|
||||
|
||||
/* stop the signaling */
|
||||
|
||||
/* This is a forced stopped */
|
||||
ftdm_clear_flag(span, FTDM_SPAN_NON_STOPPABLE);
|
||||
|
||||
ftdm_span_stop(span);
|
||||
|
||||
/* destroy the channels */
|
||||
@@ -739,6 +743,11 @@ FT_DECLARE(ftdm_status_t) ftdm_span_stop(ftdm_span_t *span)
|
||||
ftdm_status_t status = FTDM_SUCCESS;
|
||||
|
||||
ftdm_mutex_lock(span->mutex);
|
||||
|
||||
if (ftdm_test_flag(span, FTDM_SPAN_NON_STOPPABLE)) {
|
||||
status = FTDM_NOTIMPL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!ftdm_test_flag(span, FTDM_SPAN_STARTED)) {
|
||||
status = FTDM_EINVAL;
|
||||
@@ -2288,7 +2297,8 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch
|
||||
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1);
|
||||
break;
|
||||
case FTDM_CHANNEL_INDICATE_PROCEED:
|
||||
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) {
|
||||
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE) ||
|
||||
ftdmchan->state == FTDM_CHANNEL_STATE_PROCEED) {
|
||||
ftdm_ack_indication(ftdmchan, indication, status);
|
||||
goto done;
|
||||
}
|
||||
@@ -2929,23 +2939,27 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
|
||||
case FTDM_COMMAND_ENABLE_DTMF_DETECT:
|
||||
{
|
||||
/* if they don't have thier own, use ours */
|
||||
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
|
||||
teletone_dtmf_detect_init (&ftdmchan->dtmf_detect, ftdmchan->rate);
|
||||
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT);
|
||||
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF);
|
||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Enabled software DTMF detector\n");
|
||||
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
||||
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
|
||||
teletone_dtmf_detect_init (&ftdmchan->dtmf_detect, ftdmchan->rate);
|
||||
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT);
|
||||
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF);
|
||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Enabled software DTMF detector\n");
|
||||
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FTDM_COMMAND_DISABLE_DTMF_DETECT:
|
||||
{
|
||||
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
|
||||
teletone_dtmf_detect_init (&ftdmchan->dtmf_detect, ftdmchan->rate);
|
||||
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT);
|
||||
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF);
|
||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Disabled software DTMF detector\n");
|
||||
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
||||
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
|
||||
teletone_dtmf_detect_init (&ftdmchan->dtmf_detect, ftdmchan->rate);
|
||||
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT);
|
||||
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF);
|
||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Disabled software DTMF detector\n");
|
||||
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1094,6 +1094,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config)
|
||||
ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE);
|
||||
ftdm_set_flag(span, FTDM_SPAN_USE_PROCEED_STATE);
|
||||
ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES);
|
||||
ftdm_set_flag(span, FTDM_SPAN_NON_STOPPABLE);
|
||||
|
||||
if (span->trunk_type == FTDM_TRUNK_BRI_PTMP ||
|
||||
span->trunk_type == FTDM_TRUNK_BRI) {
|
||||
|
||||
@@ -133,13 +133,12 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
|
||||
get_redir_num(ftdmchan, &conEvnt->redirNmb);
|
||||
get_calling_subaddr(ftdmchan, &conEvnt->cgPtySad);
|
||||
get_prog_ind_ie(ftdmchan, &conEvnt->progInd);
|
||||
get_facility_ie(ftdmchan, &conEvnt->facilityStr);
|
||||
get_facility_ie(ftdmchan, &conEvnt->facilityStr);
|
||||
|
||||
if (get_calling_name_from_display(ftdmchan, &conEvnt->display) != FTDM_SUCCESS) {
|
||||
get_calling_name_from_usr_usr(ftdmchan, &conEvnt->usrUsr);
|
||||
}
|
||||
|
||||
|
||||
ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Incoming call: Called No:[%s] Calling No:[%s]\n", ftdmchan->caller_data.dnis.digits, ftdmchan->caller_data.cid_num.digits);
|
||||
|
||||
if (conEvnt->bearCap[0].eh.pres) {
|
||||
@@ -147,10 +146,17 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
|
||||
ftdmchan->caller_data.bearer_capability = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].infoTranCap.val);
|
||||
}
|
||||
|
||||
|
||||
if (conEvnt->shift11.eh.pres && conEvnt->ni2OctStr.eh.pres) {
|
||||
if (conEvnt->ni2OctStr.str.len == 4 && conEvnt->ni2OctStr.str.val[0] == 0x37) {
|
||||
snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", conEvnt->ni2OctStr.str.val[3]);
|
||||
uint8_t encoding = (conEvnt->ni2OctStr.str.val[2] >> 5);
|
||||
if (encoding == 0 || encoding == 1) {
|
||||
/* BCD even or BCD odd */
|
||||
uint8_t value = (conEvnt->ni2OctStr.str.val[3] & 0x0F)*10 + ((conEvnt->ni2OctStr.str.val[3] >> 4) & 0x0F);
|
||||
snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", value);
|
||||
} else if (encoding == 2) {
|
||||
/* IA 5 */
|
||||
snprintf(ftdmchan->caller_data.aniII, 5, "%c", conEvnt->ni2OctStr.str.val[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ void sngisdn_snd_setup(ftdm_channel_t *ftdmchan)
|
||||
ConEvnt conEvnt;
|
||||
sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
|
||||
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
|
||||
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_ORIG_NISDN};
|
||||
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
|
||||
|
||||
ftdm_assert((!sngisdn_info->suInstId && !sngisdn_info->spInstId), "Trying to call out, but call data was not cleared\n");
|
||||
|
||||
|
||||
@@ -186,6 +186,9 @@ typedef enum {
|
||||
/* If this flag is set, the signalling module supports jumping directly to state up, without
|
||||
going through PROGRESS/PROGRESS_MEDIA */
|
||||
FTDM_SPAN_USE_SKIP_STATES = (1 << 12),
|
||||
/* If this flag is set, then this span cannot be stopped individually, it can only be stopped
|
||||
on freetdm unload */
|
||||
FTDM_SPAN_NON_STOPPABLE = (1 << 13),
|
||||
} ftdm_span_flag_t;
|
||||
|
||||
/*! \brief Channel supported features */
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <winsock.h>
|
||||
#include <errno.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "xmlrpc_config.h"
|
||||
#include "xmlrpc-c/util_int.h"
|
||||
@@ -249,6 +248,7 @@ struct socketWin {
|
||||
SOCKET winsock;
|
||||
bool userSuppliedWinsock;
|
||||
/* 'socket' was supplied by the user; it belongs to him */
|
||||
HANDLE interruptEvent;
|
||||
};
|
||||
|
||||
static
|
||||
@@ -322,6 +322,8 @@ channelDestroy(TChannel * const channelP) {
|
||||
if (!socketWinP->userSuppliedWinsock)
|
||||
closesocket(socketWinP->winsock);
|
||||
|
||||
CloseHandle(socketWinP->interruptEvent);
|
||||
|
||||
free(socketWinP);
|
||||
}
|
||||
|
||||
@@ -430,9 +432,9 @@ channelWait(TChannel * const channelP,
|
||||
timedOut = TRUE;
|
||||
break;
|
||||
case -1: /* socket error */
|
||||
if (errno != EINTR)
|
||||
if (WSAGetLastError() != WSAEINTR)
|
||||
failed = TRUE;
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
if (FD_ISSET(socketWinP->winsock, &rfds))
|
||||
readRdy = TRUE;
|
||||
@@ -460,7 +462,9 @@ channelInterrupt(TChannel * const channelP) {
|
||||
now or in the future.
|
||||
|
||||
Actually, this is just a no-op because we don't yet know how to
|
||||
accomplish that.
|
||||
accomplish that. (But we could probably do it the same way
|
||||
chanSwitchInterrupt() works -- no one has needed it enough yet to do that
|
||||
work).
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
}
|
||||
@@ -484,7 +488,7 @@ ChannelWinGetPeerName(TChannel * const channelP,
|
||||
|
||||
if (rc != 0) {
|
||||
int const lastError = WSAGetLastError();
|
||||
xmlrpc_asprintf(errorP, "getpeername() failed. WSAERROR = %d (%s)",
|
||||
xmlrpc_asprintf(errorP, "getpeername() failed. WSA error = %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
} else {
|
||||
if (addrlen != sizeof(sockAddr))
|
||||
@@ -581,7 +585,8 @@ makeChannelFromWinsock(SOCKET const winsock,
|
||||
|
||||
socketWinP->winsock = winsock;
|
||||
socketWinP->userSuppliedWinsock = TRUE;
|
||||
|
||||
socketWinP->interruptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
ChannelCreate(&channelVtbl, socketWinP, &channelP);
|
||||
|
||||
if (channelP == NULL)
|
||||
@@ -591,8 +596,10 @@ makeChannelFromWinsock(SOCKET const winsock,
|
||||
*channelPP = channelP;
|
||||
*errorP = NULL;
|
||||
}
|
||||
if (*errorP)
|
||||
if (*errorP) {
|
||||
CloseHandle(socketWinP->interruptEvent);
|
||||
free(socketWinP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,7 +639,7 @@ ChannelWinCreateWinsock(SOCKET const fd,
|
||||
socklen_t peerAddrLen;
|
||||
int rc;
|
||||
|
||||
peerAddrLen = sizeof(peerAddrLen);
|
||||
peerAddrLen = sizeof(peerAddr);
|
||||
|
||||
rc = getpeername(fd, &peerAddr, &peerAddrLen);
|
||||
|
||||
@@ -676,6 +683,8 @@ chanSwitchDestroy(TChanSwitch * const chanSwitchP) {
|
||||
if (!socketWinP->userSuppliedWinsock)
|
||||
closesocket(socketWinP->winsock);
|
||||
|
||||
CloseHandle(socketWinP->interruptEvent);
|
||||
|
||||
free(socketWinP);
|
||||
}
|
||||
|
||||
@@ -711,6 +720,49 @@ chanSwitchListen(TChanSwitch * const chanSwitchP,
|
||||
|
||||
|
||||
|
||||
static void
|
||||
createChannelForAccept(int const acceptedWinsock,
|
||||
struct sockaddr const peerAddr,
|
||||
TChannel ** const channelPP,
|
||||
void ** const channelInfoPP,
|
||||
const char ** const errorP) {
|
||||
|
||||
struct abyss_win_chaninfo * channelInfoP;
|
||||
makeChannelInfo(&channelInfoP, peerAddr, sizeof(peerAddr), errorP);
|
||||
if (!*errorP) {
|
||||
struct socketWin * acceptedSocketP;
|
||||
|
||||
MALLOCVAR(acceptedSocketP);
|
||||
|
||||
if (!acceptedSocketP)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory");
|
||||
else {
|
||||
TChannel * channelP;
|
||||
|
||||
acceptedSocketP->winsock = acceptedWinsock;
|
||||
acceptedSocketP->userSuppliedWinsock = FALSE;
|
||||
acceptedSocketP->interruptEvent =
|
||||
CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
ChannelCreate(&channelVtbl, acceptedSocketP, &channelP);
|
||||
if (!channelP)
|
||||
xmlrpc_asprintf(errorP,
|
||||
"Failed to create TChannel object.");
|
||||
else {
|
||||
*errorP = NULL;
|
||||
*channelPP = channelP;
|
||||
*channelInfoPP = channelInfoP;
|
||||
}
|
||||
if (*errorP) {
|
||||
CloseHandle(acceptedSocketP->interruptEvent);
|
||||
free(acceptedSocketP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static SwitchAcceptImpl chanSwitchAccept;
|
||||
|
||||
static void
|
||||
@@ -728,7 +780,7 @@ chanSwitchAccept(TChanSwitch * const chanSwitchP,
|
||||
*channelPP == NULL.
|
||||
-----------------------------------------------------------------------------*/
|
||||
struct socketWin * const listenSocketP = chanSwitchP->implP;
|
||||
|
||||
HANDLE acceptEvent = WSACreateEvent();
|
||||
bool interrupted;
|
||||
TChannel * channelP;
|
||||
|
||||
@@ -736,46 +788,44 @@ chanSwitchAccept(TChanSwitch * const chanSwitchP,
|
||||
channelP = NULL; /* No connection yet */
|
||||
*errorP = NULL; /* No error yet */
|
||||
|
||||
WSAEventSelect(listenSocketP->winsock, acceptEvent,
|
||||
FD_ACCEPT | FD_CLOSE | FD_READ);
|
||||
|
||||
while (!channelP && !*errorP && !interrupted) {
|
||||
HANDLE interrupts[2] = {acceptEvent, listenSocketP->interruptEvent};
|
||||
int rc;
|
||||
struct sockaddr peerAddr;
|
||||
socklen_t size = sizeof(peerAddr);
|
||||
int rc;
|
||||
|
||||
rc = WaitForMultipleObjects(2, interrupts, FALSE, INFINITE);
|
||||
if (WAIT_OBJECT_0 + 1 == rc) {
|
||||
interrupted = TRUE;
|
||||
continue;
|
||||
};
|
||||
|
||||
rc = accept(listenSocketP->winsock, &peerAddr, &size);
|
||||
|
||||
if (rc >= 0) {
|
||||
int const acceptedWinsock = rc;
|
||||
struct socketWin * acceptedSocketP;
|
||||
|
||||
MALLOCVAR(acceptedSocketP);
|
||||
createChannelForAccept(acceptedWinsock, peerAddr,
|
||||
&channelP, channelInfoPP, errorP);
|
||||
|
||||
if (!acceptedSocketP)
|
||||
xmlrpc_asprintf(errorP, "Unable to allocate memory");
|
||||
else {
|
||||
acceptedSocketP->winsock = acceptedWinsock;
|
||||
acceptedSocketP->userSuppliedWinsock = FALSE;
|
||||
|
||||
*channelInfoPP = NULL;
|
||||
|
||||
ChannelCreate(&channelVtbl, acceptedSocketP, &channelP);
|
||||
if (!channelP)
|
||||
xmlrpc_asprintf(errorP,
|
||||
"Failed to create TChannel object.");
|
||||
else
|
||||
*errorP = NULL;
|
||||
|
||||
if (*errorP)
|
||||
free(acceptedSocketP);
|
||||
}
|
||||
if (*errorP)
|
||||
closesocket(acceptedWinsock);
|
||||
} else if (errno == EINTR)
|
||||
interrupted = TRUE;
|
||||
else
|
||||
xmlrpc_asprintf(errorP, "accept() failed, errno = %d (%s)",
|
||||
errno, strerror(errno));
|
||||
} else {
|
||||
int const lastError = WSAGetLastError();
|
||||
|
||||
if (lastError == WSAEINTR)
|
||||
interrupted = TRUE;
|
||||
else
|
||||
xmlrpc_asprintf(errorP,
|
||||
"accept() failed, WSA error = %d (%s)",
|
||||
lastError, getWSAError(lastError));
|
||||
}
|
||||
}
|
||||
*channelPP = channelP;
|
||||
CloseHandle(acceptEvent);
|
||||
}
|
||||
|
||||
|
||||
@@ -787,15 +837,10 @@ chanSwitchInterrupt(TChanSwitch * const chanSwitchP) {
|
||||
/*----------------------------------------------------------------------------
|
||||
Interrupt any waiting that a thread might be doing in chanSwitchAccept()
|
||||
now or in the future.
|
||||
|
||||
Actually, this is just a no-op because we don't yet know how to
|
||||
accomplish that.
|
||||
-----------------------------------------------------------------------------*/
|
||||
struct socketWin * const socketWinP = chanSwitchP->implP;
|
||||
|
||||
if (!socketWinP->userSuppliedWinsock)
|
||||
closesocket(socketWinP->winsock);
|
||||
struct socketWin * const listenSocketP = chanSwitchP->implP;
|
||||
|
||||
SetEvent(listenSocketP->interruptEvent);
|
||||
}
|
||||
|
||||
|
||||
@@ -889,6 +934,7 @@ ChanSwitchWinCreate(uint16_t const portNumber,
|
||||
} else {
|
||||
socketWinP->winsock = winsock;
|
||||
socketWinP->userSuppliedWinsock = FALSE;
|
||||
socketWinP->interruptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
setSocketOptions(socketWinP->winsock, errorP);
|
||||
if (!*errorP) {
|
||||
@@ -899,8 +945,10 @@ ChanSwitchWinCreate(uint16_t const portNumber,
|
||||
chanSwitchPP);
|
||||
}
|
||||
|
||||
if (*errorP)
|
||||
if (*errorP) {
|
||||
CloseHandle(socketWinP->interruptEvent);
|
||||
closesocket(winsock);
|
||||
}
|
||||
}
|
||||
if (*errorP)
|
||||
free(socketWinP);
|
||||
@@ -929,7 +977,8 @@ ChanSwitchWinCreateWinsock(SOCKET const winsock,
|
||||
|
||||
socketWinP->winsock = winsock;
|
||||
socketWinP->userSuppliedWinsock = TRUE;
|
||||
|
||||
socketWinP->interruptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
ChanSwitchCreate(&chanSwitchVtbl, socketWinP, &chanSwitchP);
|
||||
|
||||
if (chanSwitchP == NULL)
|
||||
@@ -939,8 +988,10 @@ ChanSwitchWinCreateWinsock(SOCKET const winsock,
|
||||
*chanSwitchPP = chanSwitchP;
|
||||
*errorP = NULL;
|
||||
}
|
||||
if (*errorP)
|
||||
if (*errorP) {
|
||||
CloseHandle(socketWinP->interruptEvent);
|
||||
free(socketWinP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user