Skip to content

Commit 6c972e6

Browse files
Report allocated port in tcpip-forward success reply
1 parent 2d0ef5a commit 6c972e6

6 files changed

Lines changed: 177 additions & 18 deletions

File tree

examples/echoserver/echoserver.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,15 +497,15 @@ static WS_SOCKET_T connect_addr(const char* name, word16 port)
497497

498498

499499
static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
500-
const char* name, word32 port)
500+
const char* name, word32* port)
501501
{
502502
WS_AppCtx *appCtx = (WS_AppCtx *)vCtx;
503503
WS_FwdCbActionCtx* fwdCbCtx = (WS_FwdCbActionCtx *)appCtx->privateData;
504504
int ret = 0;
505505

506506
if (action == WOLFSSH_FWD_LOCAL_SETUP) {
507507
fwdCbCtx->hostName = WSTRDUP(name, NULL, 0);
508-
fwdCbCtx->hostPort = port;
508+
fwdCbCtx->hostPort = *port;
509509
fwdCbCtx->isDirect = 1;
510510
appCtx->state = APP_STATE_CONNECT;
511511
}
@@ -525,9 +525,10 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
525525
else if (action == WOLFSSH_FWD_REMOTE_SETUP) {
526526
struct sockaddr_in addr;
527527
socklen_t addrSz = 0;
528+
socklen_t boundSz = sizeof(addr);
528529

529530
fwdCbCtx->hostName = WSTRDUP(name, NULL, 0);
530-
fwdCbCtx->hostPort = port;
531+
fwdCbCtx->hostPort = *port;
531532

532533
appCtx->listenFd = socket(AF_INET, SOCK_STREAM, 0);
533534
if (appCtx->listenFd == -1) {
@@ -544,7 +545,7 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
544545

545546
addr.sin_addr.s_addr = INADDR_ANY;
546547
addr.sin_family = AF_INET;
547-
addr.sin_port = htons((word16)port);
548+
addr.sin_port = htons((word16)*port);
548549
addrSz = sizeof addr;
549550
}
550551
else {
@@ -562,6 +563,21 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
562563
ret = listen(appCtx->listenFd, 5);
563564
}
564565

566+
if (ret == 0 && *port == 0) {
567+
/* The peer requested port 0, so the OS picked the port during
568+
* bind(). Report the actual port back to the caller. */
569+
WMEMSET(&addr, 0, sizeof addr);
570+
if (getsockname(appCtx->listenFd,
571+
(struct sockaddr*)&addr, &boundSz) == 0) {
572+
*port = (word32)ntohs(addr.sin_port);
573+
fwdCbCtx->hostPort = *port;
574+
}
575+
else {
576+
printf("getsockname failed for forwarded port.\n");
577+
ret = -1;
578+
}
579+
}
580+
565581
if (ret == 0) {
566582
appCtx->state = APP_STATE_LISTEN;
567583
}
@@ -593,7 +609,7 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
593609
appCtx->state = APP_STATE_INIT;
594610
}
595611
else if (action == WOLFSSH_FWD_CHANNEL_ID) {
596-
appCtx->channelId = port;
612+
appCtx->channelId = *port;
597613
}
598614
else
599615
ret = WS_FWD_INVALID_ACTION;

ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -495,14 +495,14 @@ static WS_SOCKET_T connect_addr(const char* name, word16 port)
495495

496496

497497
static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
498-
const char* name, word32 port)
498+
const char* name, word32* port)
499499
{
500500
WS_FwdCbActionCtx* ctx = (WS_FwdCbActionCtx*)vCtx;
501501
int ret = 0;
502502

503503
if (action == WOLFSSH_FWD_LOCAL_SETUP) {
504504
ctx->hostName = WSTRDUP(name, NULL, 0);
505-
ctx->hostPort = port;
505+
ctx->hostPort = *port;
506506
ctx->isDirect = 1;
507507
ctx->state = FWD_STATE_DIRECT;
508508
}
@@ -521,9 +521,10 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
521521
else if (action == WOLFSSH_FWD_REMOTE_SETUP) {
522522
struct sockaddr_in addr;
523523
socklen_t addrSz = 0;
524+
socklen_t boundSz = sizeof(addr);
524525

525526
ctx->hostName = WSTRDUP(name, NULL, 0);
526-
ctx->hostPort = port;
527+
ctx->hostPort = *port;
527528

528529
ctx->listenFd = socket(AF_INET, SOCK_STREAM, 0);
529530
if (ctx->listenFd == -1) {
@@ -540,7 +541,7 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
540541

541542
addr.sin_addr.s_addr = INADDR_ANY;
542543
addr.sin_family = AF_INET;
543-
addr.sin_port = htons((word16)port);
544+
addr.sin_port = htons((word16)*port);
544545
addrSz = sizeof addr;
545546
}
546547
else {
@@ -558,6 +559,21 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
558559
ret = listen(ctx->listenFd, 5);
559560
}
560561

562+
if (ret == 0 && *port == 0) {
563+
/* The peer requested port 0, so the OS picked the port during
564+
* bind(). Report the actual port back to the caller. */
565+
WMEMSET(&addr, 0, sizeof addr);
566+
if (getsockname(ctx->listenFd,
567+
(struct sockaddr*)&addr, &boundSz) == 0) {
568+
*port = (word32)ntohs(addr.sin_port);
569+
ctx->hostPort = *port;
570+
}
571+
else {
572+
printf("getsockname failed for forwarded port.\n");
573+
ret = -1;
574+
}
575+
}
576+
561577
if (ret == 0) {
562578
ctx->state = FWD_STATE_LISTEN;
563579
}
@@ -589,7 +605,7 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
589605
ctx->state = FWD_STATE_INIT;
590606
}
591607
else if (action == WOLFSSH_FWD_CHANNEL_ID) {
592-
ctx->channelId = port;
608+
ctx->channelId = *port;
593609
}
594610
else
595611
ret = WS_FWD_INVALID_ACTION;

src/internal.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8782,6 +8782,7 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
87828782
int ret = WS_SUCCESS;
87838783
char* bindAddr = NULL;
87848784
word32 bindPort;
8785+
word32 requestedPort = 0;
87858786

87868787
WLOG(WS_LOG_DEBUG, "Entering DoGlobalRequestFwd()");
87878788

@@ -8800,6 +8801,7 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
88008801
}
88018802

88028803
if (ret == WS_SUCCESS) {
8804+
requestedPort = bindPort;
88038805
WLOG(WS_LOG_INFO, "Requesting forwarding%s for address %s on port %u.",
88048806
isCancel ? " cancel" : "", bindAddr, bindPort);
88058807
}
@@ -8808,7 +8810,7 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
88088810
if (ssh->ctx->fwdCb) {
88098811
ret = ssh->ctx->fwdCb(isCancel ? WOLFSSH_FWD_REMOTE_CLEANUP :
88108812
WOLFSSH_FWD_REMOTE_SETUP,
8811-
ssh->fwdCbCtx, bindAddr, bindPort);
8813+
ssh->fwdCbCtx, bindAddr, &bindPort);
88128814
}
88138815
else {
88148816
WLOG(WS_LOG_WARN, "No forwarding callback set, rejecting request. "
@@ -8817,6 +8819,27 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
88178819
}
88188820
}
88198821

8822+
if (ret == WS_SUCCESS && !isCancel) {
8823+
/* A remote forward was set up successfully. RFC 4254 7.1 requires a
8824+
* port-0 (dynamic) request to be answered with the allocated port; if
8825+
* the callback reported none we cannot comply, so undo the setup and
8826+
* reject instead of sending a success carrying port 0. */
8827+
if (requestedPort == 0 && bindPort == 0) {
8828+
int cleanupRet;
8829+
word32 cleanupPort = bindPort;
8830+
8831+
WLOG(WS_LOG_WARN, "Forward callback reported no allocated port "
8832+
"for a port-0 request; rejecting.");
8833+
cleanupRet = ssh->ctx->fwdCb(WOLFSSH_FWD_REMOTE_CLEANUP,
8834+
ssh->fwdCbCtx, bindAddr, &cleanupPort);
8835+
if (cleanupRet != WS_SUCCESS) {
8836+
WLOG(WS_LOG_WARN, "Forward cleanup after rejection failed, "
8837+
"ret = %d", cleanupRet);
8838+
}
8839+
ret = WS_FWD_SETUP_E;
8840+
}
8841+
}
8842+
88208843
if (wantReply) {
88218844
if (ret == WS_SUCCESS) {
88228845
if (isCancel) {
@@ -8830,7 +8853,7 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
88308853
ret = SendRequestSuccess(ssh, 0);
88318854
}
88328855
}
8833-
else if (ret == WS_UNIMPLEMENTED_E) {
8856+
else if (ret == WS_UNIMPLEMENTED_E || ret == WS_FWD_SETUP_E) {
88348857
/* No reply expected; silently reject without terminating connection. */
88358858
ret = WS_SUCCESS;
88368859
}
@@ -8970,6 +8993,7 @@ static int DoChannelOpen(WOLFSSH* ssh,
89708993
char* host = NULL;
89718994
char* origin = NULL;
89728995
word32 hostPort = 0, originPort = 0;
8996+
word32 channelId = 0;
89738997
int isDirect = 0;
89748998
#endif /* WOLFSSH_FWD */
89758999
WOLFSSH_CHANNEL* newChannel = NULL;
@@ -9066,10 +9090,13 @@ static int DoChannelOpen(WOLFSSH* ssh,
90669090

90679091
if (ssh->ctx->fwdCb) {
90689092
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_LOCAL_SETUP,
9069-
ssh->fwdCbCtx, host, hostPort);
9093+
ssh->fwdCbCtx, host, &hostPort);
90709094
if (ret == WS_SUCCESS) {
9095+
/* Pass a copy so the callback cannot mutate the
9096+
* channel id and corrupt channel bookkeeping. */
9097+
channelId = newChannel->channel;
90719098
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_CHANNEL_ID,
9072-
ssh->fwdCbCtx, NULL, newChannel->channel);
9099+
ssh->fwdCbCtx, NULL, &channelId);
90739100
}
90749101
}
90759102
else {

src/ssh.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2759,6 +2759,7 @@ WOLFSSH_CHANNEL* wolfSSH_ChannelFwdNewRemote(WOLFSSH* ssh,
27592759
const char* origin, word32 originPort)
27602760
{
27612761
WOLFSSH_CHANNEL* newChannel = NULL;
2762+
word32 channelId = 0;
27622763
int ret = WS_SUCCESS;
27632764

27642765
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelFwdNewRemote()");
@@ -2779,8 +2780,11 @@ WOLFSSH_CHANNEL* wolfSSH_ChannelFwdNewRemote(WOLFSSH* ssh,
27792780
ret = SendChannelOpenForward(ssh, newChannel);
27802781
if (ret == WS_SUCCESS) {
27812782
if (ssh->ctx->fwdCb) {
2783+
/* Pass a copy so the callback cannot mutate the channel id and
2784+
* corrupt channel bookkeeping. */
2785+
channelId = newChannel->channel;
27822786
ret = ssh->ctx->fwdCb(WOLFSSH_FWD_CHANNEL_ID, ssh->fwdCbCtx,
2783-
NULL, newChannel->channel);
2787+
NULL, &channelId);
27842788
}
27852789
}
27862790

tests/regress.c

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,18 @@ static void AssertGlobalRequestReply(const ChannelOpenHarness* harness,
11181118
}
11191119
}
11201120
}
1121+
1122+
static word32 ParseGlobalRequestSuccessPort(const byte* packet, word32 packetSz)
1123+
{
1124+
word32 port;
1125+
1126+
AssertNotNull(packet);
1127+
AssertTrue(packetSz >= 10);
1128+
AssertIntEQ(packet[5], MSGID_REQUEST_SUCCESS);
1129+
WMEMCPY(&port, packet + 6, sizeof(port));
1130+
1131+
return ntohl(port);
1132+
}
11211133
#endif
11221134

11231135
static int RejectChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx)
@@ -1130,7 +1142,7 @@ static int RejectChannelOpenCb(WOLFSSH_CHANNEL* channel, void* ctx)
11301142

11311143
#ifdef WOLFSSH_FWD
11321144
static int RejectDirectTcpipSetup(WS_FwdCbAction action, void* ctx,
1133-
const char* host, word32 port)
1145+
const char* host, word32* port)
11341146
{
11351147
(void)ctx;
11361148
(void)host;
@@ -1143,7 +1155,7 @@ static int RejectDirectTcpipSetup(WS_FwdCbAction action, void* ctx,
11431155
}
11441156

11451157
static int AcceptFwdCb(WS_FwdCbAction action, void* ctx,
1146-
const char* host, word32 port)
1158+
const char* host, word32* port)
11471159
{
11481160
(void)action;
11491161
(void)ctx;
@@ -1152,6 +1164,35 @@ static int AcceptFwdCb(WS_FwdCbAction action, void* ctx,
11521164

11531165
return WS_SUCCESS;
11541166
}
1167+
1168+
#define REGRESS_FWD_ALLOC_PORT 49152
1169+
1170+
static int AllocatePortFwdCb(WS_FwdCbAction action, void* ctx,
1171+
const char* host, word32* port)
1172+
{
1173+
(void)ctx;
1174+
(void)host;
1175+
1176+
if (action == WOLFSSH_FWD_REMOTE_SETUP && port != NULL && *port == 0)
1177+
*port = REGRESS_FWD_ALLOC_PORT;
1178+
1179+
return WS_SUCCESS;
1180+
}
1181+
1182+
/* Accepts the remote setup but never reports an allocated port. Records
1183+
* whether the server asks it to clean the setup back up. */
1184+
static int NoPortFwdCb(WS_FwdCbAction action, void* ctx,
1185+
const char* host, word32* port)
1186+
{
1187+
int* cleanupCalled = (int*)ctx;
1188+
(void)host;
1189+
(void)port;
1190+
1191+
if (action == WOLFSSH_FWD_REMOTE_CLEANUP && cleanupCalled != NULL)
1192+
*cleanupCalled = 1;
1193+
1194+
return WS_SUCCESS;
1195+
}
11551196
#endif
11561197

11571198

@@ -1523,6 +1564,56 @@ static void TestGlobalRequestFwdWithCbSendsSuccess(void)
15231564
FreeChannelOpenHarness(&harness);
15241565
}
15251566

1567+
static void TestGlobalRequestFwdPort0ReturnsAllocatedPort(void)
1568+
{
1569+
ChannelOpenHarness harness;
1570+
byte in[256];
1571+
word32 inSz;
1572+
int ret;
1573+
1574+
/* A bind port of 0 asks the server to allocate a port. The success reply
1575+
* must carry the port the callback allocated, not the requested 0. */
1576+
inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 0, 0, 1, in, sizeof(in));
1577+
InitChannelOpenHarness(&harness, in, inSz);
1578+
AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, AllocatePortFwdCb, NULL),
1579+
WS_SUCCESS);
1580+
1581+
ret = DoReceive(harness.ssh);
1582+
1583+
AssertIntEQ(ret, WS_SUCCESS);
1584+
AssertGlobalRequestReply(&harness, MSGID_REQUEST_SUCCESS);
1585+
AssertIntEQ(ParseGlobalRequestSuccessPort(harness.io.out, harness.io.outSz),
1586+
REGRESS_FWD_ALLOC_PORT);
1587+
1588+
FreeChannelOpenHarness(&harness);
1589+
}
1590+
1591+
static void TestGlobalRequestFwdPort0NoAllocSendsFailure(void)
1592+
{
1593+
ChannelOpenHarness harness;
1594+
byte in[256];
1595+
word32 inSz;
1596+
int ret;
1597+
int cleanupCalled = 0;
1598+
1599+
/* The peer asked the server to choose a port (0), but the callback
1600+
* accepts without reporting one. The server must reject and tear the
1601+
* setup back down rather than reply with a non-compliant port 0. */
1602+
inSz = BuildGlobalRequestFwdPacket("0.0.0.0", 0, 0, 1, in, sizeof(in));
1603+
InitChannelOpenHarness(&harness, in, inSz);
1604+
AssertIntEQ(wolfSSH_CTX_SetFwdCb(harness.ctx, NoPortFwdCb, NULL),
1605+
WS_SUCCESS);
1606+
AssertIntEQ(wolfSSH_SetFwdCbCtx(harness.ssh, &cleanupCalled), WS_SUCCESS);
1607+
1608+
ret = DoReceive(harness.ssh);
1609+
1610+
AssertIntEQ(ret, WS_SUCCESS);
1611+
AssertGlobalRequestReply(&harness, MSGID_REQUEST_FAILURE);
1612+
AssertIntEQ(cleanupCalled, 1);
1613+
1614+
FreeChannelOpenHarness(&harness);
1615+
}
1616+
15261617
static void TestGlobalRequestFwdCancelNoCbSendsFailure(void)
15271618
{
15281619
ChannelOpenHarness harness;
@@ -3416,6 +3507,8 @@ int main(int argc, char** argv)
34163507
TestGlobalRequestFwdNoCbSendsFailure();
34173508
TestGlobalRequestFwdNoCbNoReplyKeepsConnection();
34183509
TestGlobalRequestFwdWithCbSendsSuccess();
3510+
TestGlobalRequestFwdPort0ReturnsAllocatedPort();
3511+
TestGlobalRequestFwdPort0NoAllocSendsFailure();
34193512
TestGlobalRequestFwdCancelNoCbSendsFailure();
34203513
TestGlobalRequestFwdCancelWithCbSendsSuccess();
34213514
TestRequestSuccessWithPortParsesCorrectly();

wolfssh/ssh.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,10 @@ typedef enum WS_FwdCbError {
206206
WS_FWD_PEER_E,
207207
} WS_FwdCbError;
208208

209-
typedef int (*WS_CallbackFwd)(WS_FwdCbAction, void*, const char*, word32);
209+
/* The port argument is in/out: for WOLFSSH_FWD_REMOTE_SETUP a requested port
210+
* of 0 means the callback should bind an unprivileged port and write the
211+
* allocated port back so the server can report it to the peer. */
212+
typedef int (*WS_CallbackFwd)(WS_FwdCbAction, void*, const char*, word32*);
210213
typedef int (*WS_CallbackFwdIO)(WS_FwdIoCbAction, void*, word32, void*);
211214

212215

0 commit comments

Comments
 (0)