fix: pasv port chosen after availability checks

This commit is contained in:
Ugo
2025-05-26 20:24:13 +01:00
parent 5b6919a7b1
commit cdc4e4adc9
3 changed files with 183 additions and 75 deletions

View File

@ -123,6 +123,8 @@ void *connectionWorkerHandle(cleanUpWorkerArgs *args)
// Enable cancellation for this thread
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
pthread_cleanup_push(workerCleanup, args);
ftpData->clients[theSocketId].workerData.threadIsAlive = 1;
ftpData->clients[theSocketId].workerData.threadHasBeenCreated = 1;

View File

@ -257,28 +257,45 @@ void appendToDynamicStringDataType(dynamicStringDataType *dynamicString, char *t
void setRandomicPort(ftpDataType *data, int socketPosition)
{
unsigned short int randomicPort = 5000;
int i = 0;
unsigned short int randomicPort;
int maxAttempts = data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin +1;
int attempt = 0;
int conflict;
randomicPort = data->ftpParameters.connectionPortMin + (rand()%(data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin));
while (attempt++ < maxAttempts)
{
// Generate a random port in range
randomicPort = data->ftpParameters.connectionPortMin +
(rand() % (data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin + 1));
while (i < data->ftpParameters.maxClients)
// Check against other clients
conflict = 0;
for (int i = 0; i < data->ftpParameters.maxClients; ++i)
{
if (randomicPort == data->clients[i].workerData.connectionPort)
if (i != socketPosition &&
data->clients[i].workerData.connectionPort == randomicPort)
{
randomicPort = data->ftpParameters.connectionPortMin + (rand()%(data->ftpParameters.connectionPortMax - data->ftpParameters.connectionPortMin));
i = 0;
}
else
{
i++;
conflict = 1;
break;
}
}
if (conflict)
continue;
// Check if port is in use on the system
if (!isPortInUse(randomicPort))
{
data->clients[socketPosition].workerData.connectionPort = randomicPort;
my_printf("\n data->clients[%d].workerData.connectionPort = %d",
socketPosition, randomicPort);
return;
}
}
my_printf("\n data->clients[%d].workerData.connectionPort = %d", socketPosition, data->clients[socketPosition].workerData.connectionPort);
// If were here, we failed to find a port
addLog("Failed to find available random port", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
data->clients[socketPosition].workerData.connectionPort = 0;
}
int writeListDataInfoToSocket(ftpDataType *ftpData, int clientId, int *filesNumber, int commandType, DYNMEM_MemoryTable_DataType **memoryTable)
@ -676,15 +693,23 @@ void deleteListDataInfoVector(DYNV_VectorGenericDataType *theVector)
void cancelWorker(ftpDataType *data, int clientId)
{
void *pReturn;
addLog("Cancelling thread because is busy", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
if (data->clients[clientId].workerData.threadHasBeenCreated) {
addLog("Cancelling thread because it is busy", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
int returnCode = pthread_cancel(data->clients[clientId].workerData.workerThread);
if (returnCode != 0)
{
if (returnCode != 0) {
addLog("Cancelling thread ERROR", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
}
returnCode = pthread_join(data->clients[clientId].workerData.workerThread, &pReturn);
if (returnCode != 0) {
addLog("Joining thread ERROR", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
}
data->clients[clientId].workerData.threadHasBeenCreated = 0;
data->clients[clientId].workerData.workerThread = 0; // Reset thread ID
}
}
void resetWorkerData(ftpDataType *data, int clientId, int isInitialization)

View File

@ -360,6 +360,38 @@ int getMaximumSocketFd(int mainSocket, ftpDataType * ftpData)
#ifdef IPV6_ENABLED
int isPortInUse(int port) {
int sockfd;
struct sockaddr_in6 addr;
sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1; // Assume port is in use if we can't create a socket
}
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
addr.sin6_addr = in6addr_any;
int result = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
close(sockfd); // Always close after test
if (result == 0) {
return 0; // Port is free
} else if (errno == EADDRINUSE) {
return 1; // Port is in use
} else {
perror("bind");
return 1; // Other error, treat as in-use
}
}
int createSocket(ftpDataType * ftpData)
{
//my_printf("\nCreating main socket on port %d", ftpData->ftpParameters.port);
@ -515,6 +547,37 @@ int createPassiveSocket(int port)
#else
int isPortInUse(int port) {
int sockfd;
struct sockaddr_in addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return 1; // Assume port is in use if we can't create a socket
}
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
int result = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
close(sockfd); // Always close after test
if (result == 0) {
return 0; // Port is free
} else if (errno == EADDRINUSE) {
return 1; // Port is in use
} else {
perror("bind");
return 1; // Other error, treat as in-use
}
}
int createSocket(ftpDataType * ftpData)
{
//my_printf("\nCreating main socket on port %d", ftpData->ftpParameters.port);
@ -582,21 +645,23 @@ int createSocket(ftpDataType * ftpData)
int createPassiveSocket(int port)
{
int sock, returnCode;
struct sockaddr_in temp;
struct sockaddr_in serveraddr;
int max_retries = 12; // number of bind retries
int retry_delay_sec = 1; // delay between retries
//Socket creation
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket() failed");
addLog("socket failed", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
return -1;
}
temp.sin_family = AF_INET;
temp.sin_addr.s_addr = INADDR_ANY;
temp.sin_port = htons(port);
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
serveraddr.sin_addr.s_addr = INADDR_ANY;
int reuse = 1;
#ifdef SO_REUSEADDR
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0)
{
@ -609,37 +674,53 @@ int createPassiveSocket(int port)
#ifdef SO_REUSEPORT
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0)
{
perror("setsockopt(SO_REUSEADDR) failed");
my_printfError("setsockopt(SO_REUSEADDR) failed");
addLog("set socket error", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
perror("setsockopt(SO_REUSEPORT) failed");
my_printfError("setsockopt(SO_REUSEPORT) failed");
addLog("setsocketerror", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
}
#endif
//Bind socket
returnCode = bind(sock,(struct sockaddr*) &temp,sizeof(temp));
if (returnCode == -1)
// Retry bind if it fails with EADDRINUSE
for (int i = 0; i < max_retries; i++)
{
my_printf("\n Could not bind %d errno = %d", sock, errno);
if (sock != -1)
returnCode = bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (returnCode == 0)
{
if (i > 0)
printf("\n Success After: %d attempts", i);
break;
}
if (errno == EADDRINUSE)
{
my_printf("Bind failed with EADDRINUSE on port: %d, retrying %d/%d...\n", port, i + 1, max_retries);
sleep(retry_delay_sec);
}
else
{
perror("bind() failed");
addLog("bind failed", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
close(sock);
return -1;
}
return returnCode;
}
//Number of client allowed
if (returnCode != 0)
{
my_printf("Bind failed after %d retries, errno=%d\n", max_retries, errno);
addLog("bind failed after all attempts!", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
close(sock);
return -1;
}
// Start listening
returnCode = listen(sock, 1);
if (returnCode == -1)
{
my_printf("\n Could not listen %d errno = %d", sock, errno);
if (sock != -1)
{
addLog("listen failed", CURRENT_FILE, CURRENT_LINE, CURRENT_FUNC);
my_printf("\nCould not listen %d errno = %d", sock, errno);
close(sock);
}
return returnCode;
return -1;
}
return sock;